Математические функции cmath в C++: sqrt, pow, abs, ceil, floor — как правильно?
Перешёл с Python, где math всё умел сам. В C++ подключил <cmath>, но натыкаюсь на странности:
#include <cmath>
int n = -5;
cout << abs(n); // вроде ок
cout << pow(2, 10); // получаю 1024, но иногда 1023.9999??
int k = sqrt(16); // тут компилятор предупреждает
Какие основные функции есть в cmath, и почему pow и sqrt иногда дают неточный результат? Как правильно ими пользоваться, чтобы не ловить ошибки округления?
2 ответа
Ты столкнулся с главной засадой <cmath>: эти функции работают с числами с плавающей точкой (double), а не с целыми. Отсюда и 1023.9999, и предупреждение при присваивании в int.
Основные функции <cmath>:
#include <iostream>
#include <cmath>
using namespace std;
int main() {
cout << sqrt(16.0) << "\n"; // 4 — корень
cout << pow(2, 10) << "\n"; // 1024 — возведение в степень
cout << abs(-5.5) << "\n"; // 5.5 — модуль
cout << ceil(2.1) << "\n"; // 3 — округление вверх
cout << floor(2.9) << "\n"; // 2 — округление вниз
cout << round(2.5) << "\n"; // 3 — к ближайшему
cout << fmod(10, 3) << "\n"; // 1 — остаток для double
cout << log(M_E) << "\n"; // 1 — натуральный логарифм
return 0;
}
Почему pow(2,10) бывает 1023.9999: внутри pow считается через логарифм/экспоненту в double, и накапливается погрешность. Если нужна целая степень целого числа — не используй pow, пиши свою функцию или цикл:
long long ipow(long long base, int exp) {
long long r = 1;
while (exp--) r *= base;
return r;
}
Про sqrt в int: если делаешь int k = sqrt(n), из-за округления вниз может получиться на 1 меньше нужного. Безопасный приём — проверить соседей:
int k = (int)sqrt((double)n);
while ((k+1)*(k+1) <= n) k++;
while (k*k > n) k--;
Итог: для целочисленной математики избегай pow/sqrt, а где без double не обойтись — помни про погрешность и не сравнивай дробные на точное равенство.
Про abs есть отдельная путаница. Для целых чисел abs живёт в <cstdlib>, а для дробных — в <cmath>. Если подключить не тот заголовок, abs(-5) может молча привестись к double и обрезаться.
В современном C++ проще всего использовать std::abs — он перегружен и сам выберет нужный вариант:
#include <cmath>
using namespace std;
cout << abs(-5) << " " << abs(-5.5); // 5 5.5