Почему pow (10,5) = 9,999 в C ++ - PullRequest
11 голосов
/ 14 марта 2012

В последнее время я пишу блок кода:

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

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

Ответы [ 8 ]

12 голосов
/ 14 марта 2012

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

РЕДАКТИРОВАТЬ: В случае cout << pow(10.0, 5); похоже, что результат округляется, но у меня нет подтверждающего документа, подтверждающего это.

РЕДАКТИРОВАТЬ 2: Комментарий, сделанный BoBTFish и , этот вопрос подтверждает, что при прямом использовании pow(10.0, 5) в cout округляется.

2 голосов
/ 24 апреля 2015

С Здесь

Глядя на функцию 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 -mfpmath=sse -msse3 http://aleeee.qiniudn.com/change%20SSE%20in%20CB.png

2 голосов
/ 14 марта 2012

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

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

1 голос
/ 14 марта 2012

Если код в вашем первом примере - это именно тот код, который вы выполняете, значит, у вас есть библиотека с ошибками. Независимо от того, берете ли вы std::pow или C's pow, что требует удвоения, даже если выбрана двойная версия, 10 точно представляется как double. Как таковое возведение в степень точно представимо как 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.

1 голос
/ 14 марта 2012

Что происходит, функция pow возвращает двойной, так когда вы делаете это

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

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

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

0 голосов
/ 05 июля 2018

Что происходит, так это то, что ваш ответ на самом деле 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;
}
0 голосов
/ 14 марта 2012

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

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

for(int t= 0; t < 5; t++){
   double i = pow(sections, 5- t -1);  
   cout << i << endl;
}
0 голосов
/ 14 марта 2012

В глобальном пространстве имен должна быть неработающая функция pow.Тогда std::pow «автоматически» используется вместо этого во втором примере из-за ADL.

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

...