Sam język C daje ci + - * / i %, i nic więcej: nie ma operatora potęgowania, pierwiastka ani zaokrąglania. Wszystko poza arytmetyką leży w standardowej bibliotece matematycznej, zadeklarowanej w <math.h>.
Wszystkie te funkcje działają z precyzją double: przyjmują argumenty double i zwracają double. To pierwsza rzecz do zapamiętania, bo przekazanie int działa po cichu (następuje konwersja), a przypisanie wyniku do int po cichu odrzuca część ułamkową.
Pierwsze kroki i flaga -lm
Na twoim komputerze kompilacja tego kodu może się nie udać:
/usr/bin/ld: /tmp/ccXYZ.o: in function `main':
program.c:(.text+0x1a): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
To najczęstsze potknięcie z math.h i nie jest to błąd w twoim kodzie. Nagłówek dostarczył deklaracje, więc kompilator był zadowolony; implementacje leżą w osobnej bibliotece (libm), której linker na Linuksie domyślnie nie dołącza. Dodaj -lm:
gcc program.c -o program -lm
Flaga musi stać po plikach źródłowych: linkery przetwarzają wejścia po kolei, a biblioteka podana przed kodem, który jej potrzebuje, niczego nie rozwiąże. Na macOS i z MinGW na Windows funkcje matematyczne są już częścią standardowej biblioteki C, więc -lm jest zbędne (choć na macOS nieszkodliwe).
Potęgi i pierwiastki
sqrt(x) z liczby ujemnej zwraca NaN, zamiast się wysypać; zabezpiecz argument, jeśli może być ujemny. hypot warto znać, bo oblicza tę samą wartość co sqrt(x*x + y*y) bez przepełnienia, gdy x albo y jest bardzo duże.
Dla małych potęg całkowitych zwykłe mnożenie jest i szybsze, i dokładne: x * x wygrywa z pow(x, 2.0), które przechodzi przez logarytmy i może zwrócić 8.999999999999998 tam, gdzie spodziewasz się 9.
Zaokrąglanie: floor, ceil, round, trunc
Cztery funkcje, cztery różne odpowiedzi dla liczb ujemnych. Tę tabelę warto zapamiętać:
floor(x): największa liczba całkowita nie większa niżx. Zawsze przesuwa w stronę minus nieskończoności:floor(-2.3)to-3.ceil(x): najmniejsza liczba całkowita nie mniejsza niżx. Zawsze przesuwa w stronę plus nieskończoności:ceil(-2.3)to-2.round(x): najbliższa liczba całkowita, połówki od zera:round(2.5)to3,round(-2.5)to-3.trunc(x): obcina część ułamkową, przesuwając w stronę zera:trunc(-2.7)to-2.
Wszystkie cztery zwracają double. Rzutowanie na int, gdy potrzebujesz liczby całkowitej, jest w porządku, ale pamiętaj, że samo (int)x obcina, więc (int)(x + 0.5) to stary idiom "zaokrąglania", który jest błędny dla liczb ujemnych. Używaj round().
fabs i fmod
fabs to wartość bezwzględna dla liczb zmiennoprzecinkowych. Sięgnięcie zamiast niej po abs to klasyczny błąd, bo abs przyjmuje int, a konwersja dzieje się po cichu:
% w ogóle nie działa na double: to operator całkowity i kompilator go odrzuci. fmod(a, b) to jego odpowiednik zmiennoprzecinkowy i zachowuje znak a.
fabs to też właściwy sposób porównywania dwóch liczb double, bo == na liczbach zmiennoprzecinkowych to pułapka:
0.1 i 0.2 nie dają się dokładnie zapisać binarnie, więc ich suma różni się od 0.3 o włos. Porównuj z tolerancją, nigdy przez ==.
Trygonometria
Funkcje trygonometryczne działają w radianach, a nie stopniach, i to drugie niezawodne źródło złych odpowiedzi.
Pełny zestaw: sin, cos, tan, ich odwrotności asin, acos, atan, dwuargumentowe atan2(y, x) i rodzina hiperboliczna sinh, cosh, tanh. Do kątów wybieraj atan2(y, x) zamiast atan(y / x): obsługuje x == 0 i poprawnie ustala ćwiartkę.
math.h w większości systemów definiuje M_PI, ale to rozszerzenie POSIX, a nie standardowe C, więc przy -std=c17 może go brakować. Zdefiniowanie własnej stałej, jak powyżej, jest przenośne.
Logarytmy i funkcje wykładnicze
Zwróć uwagę na pułapkę w nazewnictwie: log() to logarytm naturalny (podstawa e), a nie dziesiętny. Podstawa 10 to log10().
log(0.0) zwraca minus nieskończoność, a log(-1.0) zwraca NaN. Żadne z nich nie powoduje awarii, więc zły argument po cichu wędruje przez obliczenia, aż coś wypisze nan.
INFINITY, NAN i ich sprawdzanie
Liczby zmiennoprzecinkowe mają wartości, które nie są liczbami, a math.h nadaje im nazwy:
Kluczowa jest ostatnia linia. Nie możesz napisać if (x == NAN), bo to zawsze fałsz. Użyj isnan(x). (Tożsamość x != x jest prawdziwa tylko dla NaN; często tak właśnie implementuje się isnan i to przydatne wyjście awaryjne, gdyby kiedyś trafił się zestaw narzędzi bez tej funkcji.)
Nieskończoności biorą się z przepełnienia (exp(1000.0)) i z dzielenia niezerowej liczby double przez zero, które w odróżnieniu od całkowitego dzielenia przez zero jest zdefiniowane i nie powoduje awarii.
Warianty float i long double
Każda funkcja ma trzy formy: wersję double, wersję float z przyrostkiem f i wersję long double z przyrostkiem l.
float sqrtf(float x);
double sqrt(double x);
long double sqrtl(long double x);
Używaj zwykłych wersji double, chyba że masz zmierzony powód, żeby tego nie robić: C domyślnie promuje do double, a wplatanie wariantów float zwykle kosztuje więcej na konwersjach, niż oszczędza. Przyrostek ma znaczenie w jednym miejscu: gdy obliczenia wyłącznie na float są krytyczne dla wydajności na sprzęcie bez obsługi double.
Praktyczny przykład
Kilka z tych funkcji razem: odległość między dwoma punktami i pierwiastki równania kwadratowego:
Zauważ fabs(disc) < 1e-12 zamiast disc == 0.0: ta sama zasada tolerancji co wcześniej, zastosowana tam, gdzie wyróżnik obliczony z mierzonych wartości prawie nigdy nie wypadnie dokładnie na zerze.
<math.h> to jeden nagłówek z większego zestawu narzędzi; resztę opisuje strona o bibliotece standardowej, a strona o liczbach losowych omawia rand, które leży w <stdlib.h>, a nie tutaj.
Najczęściej zadawane pytania
Jak używać funkcji matematycznych w C?
Dodaj #include <math.h> na początku pliku i wywołuj je: sqrt(16.0), pow(2.0, 10.0), fabs(-3.5). Przyjmują i zwracają double. Na Linuksie trzeba też dolinkować bibliotekę matematyczną przez -lm: gcc program.c -o program -lm.
Skąd błąd 'undefined reference to sqrt' przy kompilacji?
Nagłówek dał kompilatorowi deklarację, ale implementacja leży w osobnej bibliotece, której linker na Linuksie nie dołącza automatycznie. Dodaj -lm na końcu polecenia: gcc program.c -o program -lm. Na macOS i z MinGW na Windows kod matematyczny jest częścią standardowej biblioteki C, więc flaga nie jest potrzebna.
Czym różni się abs od fabs w C?
abs() z <stdlib.h> przyjmuje int i zwraca int. fabs() z <math.h> przyjmuje double i zwraca double. Wywołanie abs(-3.7) najpierw zamienia argument na int i daje 3, po cichu odrzucając część ułamkową. Dla wartości zmiennoprzecinkowych używaj fabs.
Jak sprawdzić NaN w C?
Użyj isnan(x) z <math.h>. Nie da się tego sprawdzić przez x == NAN, bo NaN jest różne od wszystkiego, łącznie z samym sobą. Ta osobliwość to zresztą test zapasowy: x != x jest prawdą tylko dla NaN. Do nieskończoności używaj isinf(x), a isfinite(x) sprawdza, czy to zwykła liczba.