Что является простым примером ошибки с плавающей запятой / округления? - PullRequest
27 голосов
/ 30 октября 2008

Я слышал об «ошибке» при использовании переменных с плавающей запятой. Теперь я пытаюсь решить эту головоломку и думаю, что получаю ошибку округления / с плавающей запятой. Итак, я наконец-то выясню основы ошибки с плавающей запятой.

Что представляет собой простой пример ошибки с плавающей запятой / округления (предпочтительно в C ++)?

Изменить: Например, скажем, у меня есть событие, которое имеет вероятность p успеха. Я делаю это событие 10 раз (р не меняется, и все испытания независимы). Какова вероятность ровно 2 успешных испытаний? У меня это закодировано как:

double p_2x_success = pow(1-p, (double)8) * pow(p, (double)2) * (double)choose(8, 2);

Это возможность для ошибки с плавающей запятой?

Ответы [ 6 ]

28 голосов
/ 30 октября 2008
 for(double d = 0; d != 0.3; d += 0.1); // never terminates 
26 голосов
/ 17 апреля 2011

Изображение стоит тысячи слов - попробуйте нарисовать уравнение f(k):
enter image  description here
, и вы получите такой график XY (X и Y в логарифмическом масштабе).
enter image description here
Если компьютер может представлять 32-разрядные числа с плавающей запятой без ошибки округления, то для каждого k мы должны получить ноль. Но вместо этого ошибка увеличивается с большими значениями k из-за накопления ошибок с плавающей запятой.

НТН!

11 голосов
/ 30 октября 2008

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

Целые числа сохраняются с крайним правым битом, равным 1, и каждый бит слева удваивается (2,4,8, ...). Легко видеть, что это может хранить любое целое число до 2 ^ n, где n - количество битов.

Мантисса (десятичная часть) числа с плавающей запятой сохраняется аналогичным образом, но перемещается слева направо, и каждый последующий бит равен половине значения предыдущего. (На самом деле это немного сложнее, чем сейчас, но пока подойдет).

Таким образом, такие числа, как 0,5 (1/2), легко хранить, но не каждое число <1 может быть создано путем добавления фиксированного числа дробей в форме 1/2, 1/4, 1/8,. .. </p>

Действительно простой пример - 0,1 или 1/10. Это может быть сделано с бесконечной серией (которую я не могу потрудиться, работая над ней), но всякий раз, когда компьютер хранит 0,1, сохраняется не совсем это число.

Если у вас есть доступ к Unix-машине, это легко увидеть:

Python 2.5.1 (r251:54863, Apr 15 2008, 22:57:26) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.1
0.10000000000000001
>>> 

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

(Что касается вашего примера, 0.2 - это еще одно из тех противных чисел, которые нельзя сохранить в двоичном коде IEEE, но если вы проверяете неравенства, а не равенства, например p <= 0.2, то все будет в порядке .) </p>

5 голосов
/ 30 октября 2008

Простой C, который поймал меня некоторое время назад,

char *c = "90.1000";
double d = 0;
sscanf(c,"%f",&d);
printf("%0.4f",d);

>> 90.0999

Это была функция, которая преобразовывала углы в DMS в радианы, чего не было в вышеупомянутом случае.

3 голосов
/ 17 декабря 2014

Вот тот, кто меня поймал.

 round(256.49999) == 256
roundf(256.49999) == 257

удваивается и плавает ..

0 голосов
/ 06 ноября 2018

Мне нравится это из интерпретатора Python:

Python 2.7.10 (default, Oct  6 2017, 22:29:07) 
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.1+0.2
0.30000000000000004
>>>
...