← Все вопросы

Математические функции cmath в C++: sqrt, pow, abs, ceil, floor — как правильно?

Задан 32 месяца назад705 просмотров2 ответа
6

Перешёл с 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 ответа

11
✓ Принятый ответ — помог автору

Ты столкнулся с главной засадой <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 не обойтись — помни про погрешность и не сравнивай дробные на точное равенство.

4

Про abs есть отдельная путаница. Для целых чисел abs живёт в <cstdlib>, а для дробных — в <cmath>. Если подключить не тот заголовок, abs(-5) может молча привестись к double и обрезаться.

В современном C++ проще всего использовать std::abs — он перегружен и сам выберет нужный вариант:

#include <cmath>
using namespace std;
cout << abs(-5) << " " << abs(-5.5);  // 5 5.5

Ваш ответ

Войдите, чтобы ответить на вопрос.
Поддержать проект