为什么 1+机器 epsilon + 机器 epsilon/2 不等于 1+机器 epsilon/2+ 机器 epsil



Python:

machine_epsilon = np.finfo(float).eps
first_variant = 1 + machine_epsilon + machine_epsilon/2
second_variant = 1 + machine_epsilon/2 + machine_epsilon
print ('%.20f' % first_variant)
print ('%.20f' % second_variant)

三:

double eps2 = DBL_EPSILON;
printf("1 + eps / 2 +eps %.20f n", 1. + eps2 / 2. + eps2);
printf("1 + eps +eps/2 %.20f n", 1.+ eps2 + eps2 / 2.);

它导致first_variant1.00000000000000044409second_variant1.00000000000000022204,即分数部分多了2

。谁能解释这一点?

为什么
1 + machine_epsilon + machine_epsilon/2

不等于
1 + machine_epsilon/2 + machine_epsilon?

四舍五入在表达式中以不同的值出现,导致不同的总和。


让我们以较小的步骤并使用"%a"来查看它。

double eps2 = DBL_EPSILON;
printf("%-17s:%21a +  %11a = %21an", "1 + eps/2", //
1.0, eps2 / 2.0, 1. + eps2 / 2.0);
printf("%-17s:%21a +  %11a = %21an", "(1 + eps/2) + eps", //
1.0 + eps2 / 2, eps2, 1.0 + eps2 / 2.0 + eps2);
printf("%-17s:%21a +  %11a = %21an", "1 + eps", //
1.0, eps2, 1. + eps2);
printf("%-17s:%21a +  %11a = %21an", "(1 + eps) + eps/2", //
1. + eps2, eps2, 1.0 + eps2 + eps2 / 2.0);

输出

1 + eps/2        :               0x1p+0 +      0x1p-53 =                0x1p+0
(1 + eps/2) + eps:               0x1p+0 +      0x1p-52 =  0x1.0000000000001p+0
1 + eps          :               0x1p+0 +      0x1p-52 =  0x1.0000000000001p+0
(1 + eps) + eps/2: 0x1.0000000000001p+0 +      0x1p-52 =  0x1.0000000000002p+0

考虑 3 个连续double值,A = 1.0,B = 下一个最大的double或 1.0 + eps 和下一个doubleC = 1.0 + 2*eps。

确切1 + eps/2和介于 A 和 B 之间。 使用典型的舍入(舍入到最接近,并列到偶数),总和四舍五入到最佳doubleA(向下)。 A 是数,因为它的最低有效位是 0,B 是奇数。

(1 + eps / 2) + eps和就像1 + eps或B。

1 + eps和为 B。

(1 + eps) + eps / 2和是 B 和 C 之间的一半。 对于典型的舍入(舍入到最接近,并列到偶数),总和舍入为 C(向上)。 C 是数,因为它的最低有效位是 0,B 是奇数。


如果2printf("%dn", FLT_EVAL_METHOD);,则存在其他可能性。 在这种情况下,此处的所有 C 浮点数学都将使用long double作为中间结果,然后最终的第 1、2 个变体double结果将预期相同。

浮点运算有限制,它基于 IEEE 754 标准。由于您需要对数字进行四舍五入才能显示它们,因此100/3 = 33.333333333333336举个例子,但在现实世界中,它并不33.333333333333336。我的意思是它不仅适用于机器 epsilon,让我们举个例子

print(1 + 1.05/2 + 1.05,1 + 1.05 + 1.05/2)
>>> (2.575, 2.5749999999999997)
print(1 + 1.05/2 + 1.05,1 + (1.05 + 1.05/2))
>>> (2.575, 2.575)

第二个示例给出了相同的输出,(1.05 + 1.05/2)加 1

你得到不同的输出,因为计算顺序,解释如下,

a = 1 + machine_epsilon
print(a, a +  machine_epsilon/2)
>>> (1.0000000000000002, 1.0000000000000004)
a = 1 + machine_epsilon/2
print(a,a + machine_epsilon)
>>> (1.0, 1.0000000000000002)

因此,您应该考虑添加括号,

import numpy as np
machine_epsilon = np.finfo(float).eps
first_variant = 1 + machine_epsilon + machine_epsilon/2
second_variant = 1 + (machine_epsilon/2 + machine_epsilon)
print(first_variant,second_variant)
>>> (1.0000000000000004, 1.0000000000000004)

欲了解更多详情,请参阅, 浮点数学坏了吗?

浮点类型,如 C 中的doublefloat,具有有限的精度(例如,双精度 IEEE 754 二进制 64 的 53 位有效位),因此它们不能表示所有实数,并且它们所涉及的数学运算不具有精确计算中预期的所有属性,特别是它们不是可交换的。

OP 正在尝试评估

1 + machine_epsilon + machine_epsilon/2

这很有挑战性,因为

  • 该数字是不能用double(或浮点数)准确表示的数字之一,而1 + machine_epsilon1 + 2 * machine_epsilon都可以。

  • 计算表达式时,即使硬件可以使用更高的精度执行计算和计算中间值,存储的结果也必须舍入为可表示的值之一。四舍五入到最接近有许多提示,例如远离零的领带或从偶数开始的领带。

在发布的示例中,1 + machine_epsilon/2四舍五入为1,然后加上machine_epsilon,结果变为1 + machine_epsilon,而1 + machine_epsilon + machine_epsilon/2四舍五入为1 + 2 * machine_epsilon

你可能在学校学到的一件事是,加法是可交换的和关联的:顺序并不重要。 在纯数学中,a + b == b + a,和(a + b) + c== a+(b + c)。

但是,关于我们每天在计算机上使用的有限精度浮点运算,更令人惊讶的事情之一是加法不是可交换的或关联的。 很多时候,顺序确实很重要,这是你看到的惊人结果的部分解释。

例如,有时会发生的情况是,当b很小时,a + b最终等于a- 加上b最终根本没有显着差异。 但是对于其他一些稍大的值ca + c最终可能会与a不同,然后值a + c使得a + c + b实际上与a + c不同。

而且,由于在您的情况下,您选择了机器"epsilon"附近的bc,因此您几乎可以保证您将在将会发生如此令人惊讶的舍入结果的区域进行操作。

不过,我无法解释为什么你在C和Python中看到不同的结果。

最新更新