2010-05-26 6 views
3

Можно создать дубликат:
Floating point inaccuracy examplesC++ с плавающей точкой точность

double a = 0.3; 
std::cout.precision(20); 
std::cout << a << std::endl; 

результат: 0,2999999999999999889

double a, b; 
a = 0.3; 
b = 0; 
for (char i = 1; i <= 50; i++) { 
    b = b + a; 
}; 
std::cout.precision(20); 
std::cout << b << std::endl; 

результат: +15,000000000000014211

Так что. 'A' меньше, чем должно быть. Но если мы возьмем «а» 50 раз, результат будет больше, чем должен быть.

Почему это? И как получить правильный результат в этом случае?

+4

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

+0

, чтобы иметь точные значения, вместо этого используйте целые числа (или некоторые bignum lib) const int acc = 100; int tmp, a = 30/acc, b = 0; для (char i = 1; i <= 50; i ++) b = b + a; std :: cout << int (b/acc) << "."; TMP = Ь% соотв; if (tmp <10) std :: cout << "0"; std :: cout << int (tmp); чтобы ускорить работу, вы можете использовать мощность 2 для acc так *, /,% конвертируется в <<,>>, & – Spektre

ответ

14

Чтобы получить правильные результаты, дон 't установить точность больше, чем доступно для этого числового типа:

#include <iostream> 
#include <limits> 
int main() 
{ 
     double a = 0.3; 
     std::cout.precision(std::numeric_limits<double>::digits10); 
     std::cout << a << std::endl; 
     double b = 0; 
     for (char i = 1; i <= 50; i++) { 
        b = b + a; 
     }; 
     std::cout.precision(std::numeric_limits<double>::digits10); 
     std::cout << b << std::endl; 
} 

Хотя, если этот цикл работает для 5000 итераций вместо 50, накопленная ошибка будет отображаться даже при таком подходе - именно так работают числа с плавающей запятой.

+4

+1 для numeric_limits окончательно недостаточно известны, как они должны быть. –

5

Компьютеры хранят номера с плавающей точкой в ​​двоичном, а не десятичном формате.

Многие числа, которые выглядят обычными в десятичной форме, например 0,3, не имеют точного представления конечной длины в двоичном формате.
Поэтому компилятор выбирает самое близкое число, которое имеет точное двоичное представление, так же, как вы пишете 0.33333 для 1⁄3.

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

+2

Я думаю, что ключевым ответом для OP является: «вы не можете получить правильный результат с помощью плавающей запятой». :) –

+3

Вы получаете правильный результат. Придерживайтесь, а не результат, которого вы ожидали. – MSalters

1

Дело не в том, что оно больше или меньше, просто физически невозможно хранить «0,3» в качестве точного значения внутри двоичного числа с плавающей запятой.

Способ получения «правильного» результата - не отображать 20 знаков после запятой.

+0

0,3 или 0,3333 ....? –

-2

Чтобы получить результат "правильный", попробуйте

Список арифметических библиотек Произвольное точности из Википедии: http://en.wikipedia.org/wiki/Arbitrary-precision

или

http://speleotrove.com/decimal

+2

Десятичная и произвольная арифметика, скорее всего, не нужны для целей ОП. – dan04

8

Почему это?

Поскольку числа с плавающей запятой хранятся в двоичном формате, в которых 0,3 0,01001100110011001 ... повторяется точно так же, как 1/3 равно 0,3333333 ..., повторяется в десятичной форме. Когда вы пишете 0.3, вы фактически получаете 0,29999999999999999988897769753748434595763683319091796875 (бесконечное двоичное представление округлено до 53 значащих цифр).

Имейте в виду, что для приложений, для которых с плавающей запятой сконструировано, не проблема, что вы не можете точно представлять 0.3.С плавающей точкой была разработана для использования с:

  • физических измерениями, которые часто измеряются только 4 Инжира и никогда более чем 15
  • трансцендентные функции, такие как логарифмы и тригонометрических функции, которые все равно приближаются.

Для которых двоично-десятичные преобразования практически не имеют отношения к другим источникам ошибок.

Теперь, если вы пишете финансовое программное обеспечение, для которого $ 0,30 означает точно $ 0.30, все по-другому. Для этой ситуации существуют десятичные арифметические классы.

И как получить правильный результат в этом случае?

Ограничение точности до 15 значащих цифр обычно достаточно, чтобы скрыть цифры «шум». Если вы на самом деле не нуждаетесь в точный ответ, это, как правило, лучший подход.

+0

Возможно, вы могли бы добавить к концу своего первого абзаца: «... потому что бесконечная серия двоичного представления 0,3 в какой-то момент обрезана, потому что бесконечные значения не могут быть сохранены на компьютере». – josch

Смежные вопросы