Почему моя целая математика с 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)?

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].

  • Когда следует использовать ключевое слово "strictfp" в java?
  • Двойной расчет, создающий нечетный результат
  • Является ли плавающая точка == когда-либо ОК?
  • Библиотека высокой точности с плавающей запятой Java
  • Сравнение поплавков и удвоений IEEE для равенства
  • Почему «f» требуется при объявлении float?
  • Странное поведение при приведении float в int в C #
  • Как вы печатаете EXACT значение числа с плавающей запятой?
  • Float и двойной тип данных в Java
  • Почему преобразование из float в double изменяет значение?
  • Каков всеобъемлющий диапазон float и double в Java?
  • Interesting Posts

    Сделайте ASP.NET конвертер WCF в JSON, опустив tags «Key» и «Value»

    MongoDB – запрос последнего элемента массива?

    Весна PropertyPlaceholderConfigurer и контекст: свойство-placeholder

    Не исчез ли метод List .ForEach ()?

    qplot ggplot не выполняется при поиске

    Весовые данные JPA-запрос с параметрическими свойствами

    Преобразование String to Date в .NET, если мой формат даты в формате YYYYMMDD

    Уменьшение использования памяти приложений .NET?

    Метод getText () должен быть вызван из streamа пользовательского интерфейса (Android Studio)

    Решение javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed Ошибка?

    Размер фона с SVG в IE9-10

    Как я могу определить, как пользователь нажимает клавишу HOME в моей деятельности?

    Есть ли функция Notepad ++ для добавления некоторого вертикального пространства в нижнюю часть или прокрутка ниже последней строки?

    Контекстное меню для добавления controllerа / просмотра отсутствует

    Как использовать переменную как имя поля в mongodb-native findOne ()?

    Давайте будем гением компьютера.