Почему моя целая математика с std :: pow дает неправильный ответ?
Рассмотрим следующий fragment кода:
#include #include int main() { int i = 23; int j = 1; int base = 10; int k = 2; i += j * pow(base, k); std::cout << i << std::endl; }
Он выводит «122» вместо «123». Это ошибка в g ++ 4.7.2 (MinGW, Windows XP)?
- Как анализировать float с двумя десятичными знаками в javascript?
- Странное поведение при приведении float в int в C #
- Является ли плавающая точка == когда-либо ОК?
- Почему добавление 0,1 несколько раз остается без потерь?
- Преобразовать float в строку с заданной точностью и количеством десятичных цифр?
- Почему «f» требуется при объявлении float?
- Float и двойной тип данных в Java
- Точное хранение больших целых чисел
- Как преобразовать целое число в float в Java?
- Могут ли какие-либо процессоры реального мира не использовать IEEE 754?
- Как мне сделать сравнение с плавающей запятой?
- Проблемы сравнения с плавающей запятой MySQL
- double или float, что быстрее?
std::pow()
работает с числами с плавающей запятой, которые не имеют бесконечной точности, и, вероятно, реализация стандартной библиотеки, которую вы используете, реализует pow()
в (плохой) способ, который делает эту нехватку бесконечной точности актуальной.
Однако вы можете легко определить свою собственную версию, которая работает с целыми числами. В C ++ 11 вы даже можете сделать его constexpr
(чтобы результат мог быть вычислен во время компиляции, когда это было возможно):
constexpr int int_pow(int b, int e) { return (e == 0) ? 1 : b * int_pow(b, e - 1); }
Вот живой пример .
Хвост-рекурсивная форма (кредиты Дэну Ниссенбауму ):
constexpr int int_pow(int b, int e, int res = 1) { return (e == 0) ? res : int_pow(b, e - 1, b * res); }
Все остальные ответы до сих пор пропускают или танцуют вокруг одной и единственной проблемы в вопросе:
pow
в вашей реализации на C ++ низкое. Он возвращает неверный ответ, когда нет необходимости.
Получите лучшую реализацию на C ++ или, по крайней мере, замените в ней математические функции. Тот, на что указывает Паскаль Куок , хорош.
Не с моим хотя бы:
$ g++ --version | head -1 g++ (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2) $ ./a.out 123
IDEone также работает версия 4.7.2 и дает 123
.
Подписи pow()
от http://www.cplusplus.com/reference/cmath/pow/
double pow ( double base, double exponent ); long double pow ( long double base, long double exponent ); float pow ( float base, float exponent ); double pow ( double base, int exponent ); long double pow ( long double base, int exponent );
Вы должны установить double base = 10.0;
и double i = 23.0
.
Если вы просто напишете
#include #include int main() { int i = 23; int j = 1; int base = 10; int k = 2; i += j * pow(base, k); std::cout << i << std::endl; }
как вы думаете, на что должно ссылаться? Стандарт C ++ даже не гарантирует, что после включения cmath у вас будет функция pow в глобальной области.
Имейте в виду, что все перегрузки по крайней мере находятся в пространстве имен std
. Существуют функции pow
которые принимают целочисленный показатель, и существуют функции pow
которые принимают показатели с плавающей запятой. Вполне возможно, что ваша реализация на C ++ объявляет функцию C pow в глобальном масштабе. Эта функция принимает показатель с плавающей запятой. Дело в том, что эта функция, вероятно, будет иметь несколько ошибок приближения и округления. Например, одним из возможных способов реализации этой функции является:
double pow(double base, double power) { return exp(log(base)*power); }
Вполне возможно, что pow (10.0,2.0) дает что-то вроде 99.99999999992543453265 из-за ошибок округления и аппроксимации. В сочетании с тем фактом, что преобразование с плавающей точкой в целое число дает число до десятичной точки, это объясняет ваш результат 122, потому что 99 + 3 = 122.
Попробуйте использовать перегрузку pow, которая принимает целочисленный показатель и / или делает правильное округление от float до int. Перегрузка с целочисленным показателем может дать вам точный результат для 10-й степени.
Редактировать:
Как вы указали, попытка использовать перегрузку std :: pow (double, int) также, похоже, дает значение чуть меньше 100. Я потратил время, чтобы проверить стандарты ISO и реализацию libstdc ++, чтобы увидеть, что начиная с C ++ 11 перегрузки с целыми показателями были опущены в результате разрешения отчета 550 о дефектах . Включение поддержки C ++ 0x / C ++ 11 фактически удаляет перегрузки в реализации libstdc ++, что может объяснить, почему вы не заметили никаких улучшений.
Во всяком случае, вероятно, плохая идея полагаться на точность такой функции, особенно если речь идет о преобразовании в целое. Небольшая ошибка к нулю, очевидно, будет иметь большое значение, если вы ожидаете, что значение с плавающей запятой будет целочисленным (например, 100), а затем преобразует его в значение типа int. Таким образом, мое предложение будет писать вашу собственную функцию pow, которая берет все целые числа или проявляет особую осторожность в отношении преобразования double-> int, используя вашу собственную круглую функцию, так что небольшая ошибка торможения нуля не изменяет результат.
Ваша проблема не является ошибкой в gcc, это абсолютно точно. Это может быть ошибкой в реализации pow
, но я думаю, что ваша проблема – это просто факт, что вы используете pow
который дает неточный результат с плавающей запятой (потому что он реализован как нечто вроде exp(power * log(base));
а log(base)
никогда не будет абсолютно точной [если база не является степенью e].