Что такое простой пример ошибки с плавающей точкой/округлением?

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

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

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

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

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

Ответы

Ответ 1

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

НТН!

Ответ 2

 for(double d = 0; d != 0.3; d += 0.1); // never terminates 

Ответ 3

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

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

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

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

Действительно простой пример - 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, тогда вы будете все в порядке.)

Ответ 4

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

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

>> 90.0999

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

Ответ 5

Здесь меня поймал.

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

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

Ответ 6

Мне нравится это от интерпретатора 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
>>>

Ответ 7

супер просто:

a = 10000000.1
b = 1/10
print(a - b == 10000000)
print ('a:{0:.20f}\nb:{1:.20f}'.format(a, b))

печатает (в зависимости от платформы) что-то вроде:

False                                                                                                                                 
a:10000000.09999999962747097015                                                                                                       
b:0.10000000000000000555