Я слышал об «ошибке» при использовании переменных с плавающей запятой. Теперь я пытаюсь решить эту головоломку и, кажется, получаю ошибку округления / с плавающей запятой. Итак, я, наконец, собираюсь разобраться в основах ошибки с плавающей запятой.
Каков простой пример ошибки с плавающей запятой / округления (желательно на C++)?
Обновлено: например, скажем, у меня есть событие с вероятностью p успеха. Я делаю это упражнение 10 раз (p не меняется и все попытки независимы). Какова вероятность ровно двух успешных испытаний? Я закодировал это как:
double p_2x_success = pow(1-p, (double)8) * pow(p, (double)2) * (double)choose(8, 2);
Это возможность ошибки с плавающей запятой?
Прочтите это: blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1
См. Простой пример Java, он должен быть таким же в C: stackoverflow.com/a/15790782/254109





Как правило, ошибка с плавающей запятой относится к числу, которое не может быть сохранено в представлении с плавающей запятой 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, тогда все будет в порядке.)
Простой пример на C, который поймал меня некоторое время назад:
double d = 0;
sscanf("90.1000", "%lf", &d);
printf("%0.4f", d);
Это печатает 90.0999
Это было в функции, которая конвертировала углы в DMS в радианы.
Почему в приведенном выше случае не работает?
Как заметил анонимный пользователь, с sscanf для спецификатора преобразования «f» требуется аргумент float, а не double (однако «f» означает double в printf - да, это сбивает с толку). Чтобы заставить sscanf работать с double, следует использовать модифицированный спецификатор преобразования «lf».
for(double d = 0; d != 0.3; d += 0.1); // never terminates
Картинка стоит тысячи слов - попробуйте нарисовать уравнение f(k): 
, и вы получите такой график XY (X и Y в логарифмической шкале) .
Если компьютер может представлять 32-битные числа с плавающей запятой без ошибки округления, то для каждого k мы должны получить ноль. Но вместо этого ошибка увеличивается с большими значениями k из-за накопления ошибок с плавающей запятой.
hth!
Могу ли я добавить это изображение (переделанное, чтобы оно было SVG) в Wikipedia Commons под лицензией CC0 (ссылка на это для идеи)?
Вот один, который меня поймал:
round(256.49999) == 256
roundf(256.49999) == 257
double и float имеют разную точность, поэтому первый будет представлен как 256.49999000000003, а второй как 256.5, и поэтому округление будет по-разному.
Мне нравится это из интерпретатора 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
>>>
супер просто:
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
Это самое простое, что приходит мне на ум, которое должно работать со многими языками, просто:
0.2 + 0.1
Вот несколько примеров с REPL, которые приходят мне в голову, но должны возвращать этот результат на любом языке, совместимом с IEEE754.
Python
>>> 0.2 + 0.1
0.30000000000000004
Котлин
0.2 + 0.1
res0: kotlin.Double = 0.30000000000000004
Scala
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
Я думаю, что вам действительно нужно это: Что должен знать каждый компьютерный ученый об арифметике с плавающей запятой.