Подтвердить что ты не робот

Почему pow (10,5) = 9,999 в С++

Недавно я написал блок кода:

const int sections = 10;

for(int t= 0; t < 5; t++){
   int i = pow(sections, 5- t -1);  
   cout << i << endl;
}

И результат неверен:

9999
1000
99
10
1

Если я использую только этот код:

for(int t = 0; t < 5; t++){
    cout << pow(sections,5-t-1) << endl; 
}

Проблема больше не возникает:

10000
1000
100
10
1

Кто-нибудь дает мне объяснение? спасибо вам большое!

4b9b3361

Ответ 1

Из-за представления значений с плавающей запятой pow(10.0, 5) может быть 9999.9999999 или что-то вроде этого. Когда вы назначаете это целому числу, которое было усечено.

EDIT: в случае cout << pow(10.0, 5); похоже, что результат округлен, но сейчас у меня нет подтверждающего документа, подтверждающего это.

EDIT 2: комментарий, сделанный BoBTFish и этот вопрос, подтверждает, что когда pow(10.0, 5) используется непосредственно в cout, который округляется.

Ответ 2

При использовании с дробными показателями pow (x, y) обычно оценивается как exp(log(x)*y); такая формула будет математически корректной, если ее оценивать с бесконечной точностью, но на практике может привести к ошибкам округления. Как отмечали другие, значение 9999.999999999 при приведении к целому числу даст 9999. Некоторые языки и библиотеки используют такую ​​формулировку все время при использовании оператора экспоненциальности с показателем с плавающей запятой; другие пытаются определить, когда показатель степени является целым числом, и при необходимости используйте повторное умножение. Поиск документа для функции pow, кажется, что он должен работать, когда x отрицательный, а y не имеет дробной части (когда x отрицательный и `y равно, результат должен быть pow(-x,y), когда y нечетно, результат должен быть -pow(-x,y). Казалось бы логичным, что если y не имеет дробной части, то библиотеке, которая собирается решить проблему с отрицательным значением x должен использовать повторное умножение, но я не знаю ни одной спецификации, диктующей, что он должен.

В любом случае, если вы пытаетесь поднять целое число до мощности, почти наверняка лучше всего использовать математические вычисления целочисленных чисел для вычисления или, если целое число, которое должно быть поднято, является константой или всегда будет малым, просто используйте таблица поиска (повышение чисел от 0 до 15 любой мощностью, которая будет соответствовать 64-битовому целому, потребует только таблицы из 4 096 элементов).

Ответ 3

Случается, что функция pow возвращает double, поэтому когда вы это сделаете

int i = pow(sections, 5- t -1);  

десятичный .99999 разрезов и вы получите 9999.

в то время как печать напрямую или сравнение с 10000 не является проблемой, потому что в некотором смысле это происходит.

Ответ 4

Если код в первом примере - это точный код, который вы используете, у вас есть библиотека с ошибками. Независимо от того, выбираете ли вы std::pow или C pow, который принимает двойники, даже если выбрана двойная версия, 10 точно представляется как double. Таким образом, возведение в степень точно представимо как a double. Никакое округление или усечение или что-либо подобное не должно происходить.

С g++ 4.5 я не смог воспроизвести ваше (странное) поведение даже с помощью -ffast-math и -O3.

Теперь, что я подозреваю, это то, что sections не назначается буквальный 10 напрямую, а вместо этого считывается или вычисляется внутри, так что его значение похоже на 9.9999999999999, которое при повышении до четвертой мощности генерирует число как 9999.9999999. Затем он усекается на отображаемое целое число 9999.

В зависимости от ваших потребностей вы можете обойти либо исходный номер, либо конечный номер перед назначением в int. Например: int i = pow(sections, 5- t -1) + 0.5; // Add 0.5 and truncate to round to nearest.

Ответ 5

С здесь

Глядя на функцию pow(): double pow (double base, double exponent); мы знаем, что параметры и возвращаемое значение имеют тип double. Но переменные num, i и res относятся к типу int в приведенном выше коде, при преобразовании int в double или double в int это может привести к потере точности. Например (возможно, не строгое), блок с плавающей запятой (FPU) вычисляет pow(10, 4)=9999.99999999, затем int(9999.9999999)=9999 с помощью преобразования типа в C++.

Как это решить?

Solution1

Изменить код:

    const int num = 10;

    for(int i = 0; i < 5; ++i){
       double res = pow(num, i);
       cout << res << endl;
    }

Solution2

Замените модуль с плавающей запятой (FPU) с более высокой точностью вычислений в типе double. Например, мы используем SSE в Windows CPU. В Code :: Block 13.12 мы можем выполнить следующие шаги для достижения цели: Настройка → Настройка компилятора → Компиляция GNU GCC → Другие параметры, добавить

-mfpmath=sse -msse3

Картинка выглядит следующим образом:

add <code>-mfpmath=sse -msse3</code>
(источник: qiniudn.com)

Ответ 6

В глобальном пространстве имен должна быть некоторая разбитая функция pow. Затем std::pow "автоматически" используется вместо вашего второго примера из-за ADL.

Либо это, либо t на самом деле является числом с плавающей запятой в вашем первом примере, и вы сталкиваетесь с ошибками округления.

Ответ 7

Какой тип t в вашем первом блоке кода? Если это не int, это может быть проблема округления.

Ответ 8

Вы назначаете результат int. Это заставляет его обрезать число.

Это должно работать нормально:

for(int t= 0; t < 5; t++){
   double i = pow(sections, 5- t -1);  
   cout << i << endl;
}

Ответ 9

Получается, что ваш ответ на самом деле 99,9999, а не точно 100. Это потому, что pow является двойным. Таким образом, вы можете исправить это с помощью i = ceil(pow()).

Ваш код должен быть:

const int sections = 10;
for(int t= 0; t < 5; t++){
   int i = ceil(pow(sections, 5- t -1));  
   cout << i << endl;
}