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

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

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

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

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

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


person MrDatabase    schedule 30.10.2008    source источник
comment
Я думаю, что вам действительно нужно следующее: Что должен знать каждый компьютерный ученый о плавающих Точечная арифметика.   -  person Patrick    schedule 30.10.2008
comment
Прочтите это: blog.frama-c.com/index .php? post / 2013/05/02 / nearintf1   -  person Nicholas Wilson    schedule 03.05.2013
comment
См. Простой пример Java, он должен быть таким же в C: stackoverflow.com/a/15790782/254109   -  person xmedeko    schedule 05.11.2013


Ответы (8)


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

hth!

person Agnius Vasiliauskas    schedule 17.04.2011
comment
Могу ли я добавить это изображение (переделанное, чтобы оно было SVG) в Wikipedia Commons под лицензией CC0 (ссылка на это для идеи)? - person Martin Thoma; 13.08.2018

Как правило, ошибка с плавающей запятой относится к числу, которое не может быть сохранено в представлении с плавающей запятой 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, тогда все будет в порядке.)

person Matthew Schinckel    schedule 30.10.2008

Вот один, который меня поймал:

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

числа типа double и float имеют разную точность, поэтому первое будет представлено как 256.49999000000003, а второе как 256.5, и поэтому округление будет по-разному.

person Rory O'Bryan    schedule 17.12.2014

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

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

Это напечатает 90.0999

Это было в функции, которая конвертировала углы в DMS в радианы.

Почему в приведенном выше случае не работает?

person SmacL    schedule 30.10.2008
comment
Как заметил анонимный пользователь, с sscanf для спецификатора преобразования f требуется аргумент float, а не double (однако f означает double в printf - да, это сбивает с толку). Чтобы заставить sscanf работать с double. - person Dan Moulding; 07.10.2011

Мне нравится это из интерпретатора 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
>>>
person Samuel Li    schedule 06.11.2018

Это самое простое, что приходит мне в голову, которое должно работать со многими языками, просто:

0.2 + 0.1

Вот несколько примеров с REPL, которые приходят мне в голову, но должны возвращать этот результат на любом языке, совместимом с IEEE754.

Python

>>> 0.2 + 0.1
0.30000000000000004

Котлин

0.2 + 0.1
res0: kotlin.Double = 0.30000000000000004

Скала

scala> 0.2 + 0.1
val res0: Double = 0.30000000000000004

Ява

jshell> 0.2 + 0.1
$1 ==> 0.30000000000000004

Рубин

irb(main):001:0> 0.2 + 0.1
=> 0.30000000000000004
person Danilo Pianini    schedule 20.05.2020
comment
Фактически, Lua и PHP возвращают 0.3. - person x-yuri; 10.05.2021
comment
И Perl тоже :) - person x-yuri; 10.05.2021
comment
Но работает в Javascript;) - person Danilo Pianini; 12.05.2021

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

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 
person formiaczek    schedule 14.08.2019
comment
Что это за язык? Вопрос помечен тегом C ++, где этот код вообще не имеет смысла (1/10 точно представлен в формате с плавающей запятой, это 0.00000) - person Ben Voigt; 11.05.2021