Что такое простой пример ошибки с плавающей точкой/округлением?
Я слышал о "ошибке" при использовании переменных с плавающей запятой. Теперь я пытаюсь решить эту загадку, и я думаю, что получаю ошибку округления/с плавающей запятой. Поэтому я, наконец, собираюсь выяснить основы ошибки с плавающей запятой.
Что такое простой пример ошибки с плавающей точкой/округлением (желательно на С++)?
Изменить: например, скажем, у меня есть событие, у которого вероятность p преуспевает. Я делаю это событие 10 раз (p не меняется, и все испытания независимы). Какова вероятность ровно двух успешных испытаний? У меня это закодировано как:
double p_2x_success = pow(1-p, (double)8) * pow(p, (double)2) * (double)choose(8, 2);
Является ли это возможностью для ошибки с плавающей запятой?
Ответы
Ответ 1
Изображение стоит тысячи слов - попробуйте нарисовать уравнение f(k)
:
и вы получите такой график XY (X и Y находятся в логарифмическом масштабе).
Если компьютер может представлять 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