Ссылка Мацери на окончательный ресурс по арифметике с плавающей запятой действительно является окончательным ответом на этот вопрос. Однако для завершения:
octave:34> fprintf("%.80f\n%.80f\n", 0.95, 1 - 0.05)
0.94999999999999995559107901499373838305473327636718750000000000000000000000000000
0.94999999999999995559107901499373838305473327636718750000000000000000000000000000
octave:35> fprintf("%.80f\n%.80f\n", 0.05, 1 - 0.95)
0.05000000000000000277555756156289135105907917022705078125000000000000000000000000
0.05000000000000004440892098500626161694526672363281250000000000000000000000000000
Другими словами, 0,95 труднее представить точно в плавающей запятой, поэтому любой расчет на первом шаге, который включает 0,95 (либо как вход, либо как выход), обязательно менее точен, чем тот, который использует только 0,05.
Таким образом:
1 - 0.05 = 0.95 (imprecise, due to intrinsic floating-point representation)
(1 - 0.05) - 0.95 = exactly 0 (since both are represented identically imprecisely)
vs
1 - 0.95 = imprecise 0.05 (due to involvement of 0.95 in calculation)
(imprecise 0.05) - (precise 0.05) = not exactly 0 (due to difference in precisions)
ОДНАКО . Следует отметить, что это различие в точности намного ниже допуска машины (как возвращено eps
- 2.2204e-16 на моей машине). Следовательно, для всех практических применений 4.1633e-17 равно 0. Если практическим моментом здесь является проверка того, является ли результат вычисления эффективно 0, то с практической точки зрения всегда нужно учитывать точность машины при работе с вычислениями с плавающей запятой или, предпочтительно, найти способ переформулировать вашу проблему так, чтобы она вообще не требовала проверки на равенство.