Само ядро языка C даёт вам + - * / и %, и больше ничего - ни оператора возведения в степень, ни квадратного корня, ни округления. Всё, что выходит за рамки арифметики, живёт в стандартной математической библиотеке и объявлено в <math.h>.
Все эти функции работают с точностью double: принимают аргументы типа double и возвращают double. Это первое, что стоит усвоить, потому что передача int проходит молча (он преобразуется), а вот присваивание результата в int молча отбрасывает дробную часть.
С чего начать и флаг -lm
На вашей собственной машине компиляция этого кода может завершиться неудачей:
/usr/bin/ld: /tmp/ccXYZ.o: in function `main':
program.c:(.text+0x1a): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
Это самая частая запинка при работе с math.h, и это не ошибка в вашем коде. Заголовок предоставил объявления, так что компилятор остался доволен; реализации же лежат в отдельной библиотеке (libm), которую компоновщик в Linux по умолчанию не подключает. Добавьте -lm:
gcc program.c -o program -lm
Флаг должен идти после файлов с исходным кодом: компоновщик обрабатывает входные данные по порядку, и библиотека, указанная раньше нуждающегося в ней кода, ничего не разрешит. В macOS и в MinGW под Windows математические функции уже входят в стандартную библиотеку C, так что -lm не нужен (хотя в macOS он безвреден).
Степени и корни
sqrt(x) от отрицательного числа возвращает NaN, а не падает; если аргумент может оказаться отрицательным, проверяйте его. hypot стоит знать потому, что она вычисляет то же значение, что и sqrt(x*x + y*y), но без переполнения, когда x или y очень велики.
Для небольших целых степеней обычное умножение и быстрее, и точнее: x * x выигрывает у pow(x, 2.0), которая идёт через логарифмы и может вернуть 8.999999999999998 там, где вы ждали 9.
Округление: floor, ceil, round, trunc
Четыре функции и четыре разных ответа для отрицательных чисел. Именно эту таблицу и стоит запомнить:
floor(x)- наибольшее целое, не превосходящееx. Всегда движется к минус бесконечности:floor(-2.3)равно-3.ceil(x)- наименьшее целое, не меньшееx. Всегда движется к плюс бесконечности:ceil(-2.3)равно-2.round(x)- ближайшее целое, половинки округляются от нуля:round(2.5)равно3,round(-2.5)равно-3.trunc(x)- отбрасывает дробную часть, двигаясь к нулю:trunc(-2.7)равно-2.
Все четыре возвращают double. Приводить результат к int, когда нужно целое, нормально, но учтите: само по себе (int)x выполняет усечение, поэтому старая идиома «округления» (int)(x + 0.5) неверна для отрицательных чисел. Используйте round().
fabs и fmod
fabs - это модуль для чисел с плавающей точкой. Потянуться вместо неё за abs - классическая ошибка, потому что abs принимает int и преобразование происходит молча:
% вообще не работает с double - это целочисленный оператор, и компилятор его отвергнет. fmod(a, b) - его аналог для плавающей точки, и она сохраняет знак a.
fabs - ещё и правильный способ сравнивать два double, поскольку == для плавающей точки является ловушкой:
0.1 и 0.2 невозможно точно представить в двоичном виде, поэтому их сумма на волосок отличается от 0.3. Сравнивайте с допуском, а не через ==.
Тригонометрия
Тригонометрические функции работают в радианах, а не в градусах - второй надёжный источник неверных ответов.
Полный набор: sin, cos, tan, обратные к ним asin, acos, atan, двухаргументная atan2(y, x) и гиперболическое семейство sinh, cosh, tanh. Для углов предпочитайте atan2(y, x) вместо atan(y / x) - она обрабатывает случай x == 0 и правильно определяет четверть.
В большинстве систем math.h определяет M_PI, но это расширение POSIX, а не стандартный C, поэтому под -std=c17 его может не оказаться. Собственная константа, как выше, переносима.
Логарифмы и экспоненты
Обратите внимание на ловушку в названиях, унаследованную из математики: log() - это натуральный логарифм (по основанию e), а не десятичный. Десятичный - это log10().
log(0.0) возвращает минус бесконечность, а log(-1.0) - NaN, и ни то, ни другое не приводит к падению. А значит, плохой аргумент будет молча путешествовать по вашим вычислениям, пока что-нибудь не напечатает nan.
INFINITY, NAN и проверки на них
У плавающей точки есть значения, которые числами не являются, и math.h даёт им имена:
Последняя строка - самая важная. Писать if (x == NAN) нельзя: это всегда ложь. Используйте isnan(x). (Тождество x != x истинно только для NaN - именно так isnan часто и реализована, и это удобный запасной вариант, если вам попадётся инструментарий без неё.)
Бесконечности приходят из переполнения (exp(1000.0)) и из деления ненулевого double на ноль, которое - в отличие от целочисленного деления на ноль - определено и не приводит к падению.
Варианты для float и long double
У каждой функции есть три формы: версия для double, версия для float с суффиксом f и версия для long double с суффиксом l.
float sqrtf(float x);
double sqrt(double x);
long double sqrtl(long double x);
Используйте обычные double-версии, если у вас нет измеренной причины поступить иначе: double - это тип, к которому C повышает значения по умолчанию, и подмешивание float-вариантов обычно обходится в преобразования дороже, чем экономит. Единственное место, где суффикс важен, - когда чисто float-вычисление критично по производительности на железе без поддержки double.
Практический пример
Соберём несколько функций вместе - расстояние между двумя точками и корни квадратного уравнения:
Обратите внимание на fabs(disc) < 1e-12 вместо disc == 0.0 - то же правило допуска, что и раньше, применённое там, где дискриминант, посчитанный по измеренным данным, почти никогда не попадёт ровно в ноль.
<math.h> - лишь один заголовок в большом наборе инструментов; страница стандартная библиотека описывает остальные, а случайные числа рассказывают про rand, который живёт в <stdlib.h>, а не здесь.
Часто задаваемые вопросы
Как пользоваться математическими функциями в C?
Добавьте #include <math.h> в начало файла и вызывайте их: sqrt(16.0), pow(2.0, 10.0), fabs(-3.5). Они принимают и возвращают double. В Linux нужно ещё подключить математическую библиотеку флагом -lm: gcc program.c -o program -lm.
Почему при компиляции возникает ошибка 'undefined reference to sqrt'?
Заголовок дал компилятору объявление, но реализация лежит в отдельной библиотеке, которую компоновщик в Linux сам не подключает. Добавьте -lm в конец команды: gcc program.c -o program -lm. В macOS и в MinGW под Windows математический код входит в стандартную библиотеку C, поэтому флаг не нужен.
Чем abs отличается от fabs в C?
abs() из <stdlib.h> принимает int и возвращает int. fabs() из <math.h> принимает double и возвращает double. Вызов abs(-3.7) сначала приведёт аргумент к int и вернёт 3, молча отбросив дробную часть, - для чисел с плавающей точкой используйте fabs.
Как проверить значение на NaN в C?
Используйте isnan(x) из <math.h>. Проверка x == NAN не работает, потому что NaN не равен ничему, включая самого себя, - и как раз эта странность служит запасным тестом: x != x истинно только для NaN. Для бесконечностей есть isinf(x), а isfinite(x) проверяет, что число обычное.