浮点运算:误差的求和与乘法

Floating point arithmetic: summation versus multiplication of error

我试图理解这个简单示例背后的浮点运算。这两种代码在理论上在算术上是等价的,但显然一系列的加法比简单的乘法增加了更多的错误。

s=0.0
for i in range(10):
    s += 0.1
print(s)
print('%.30f' % s)

0.9999999999999999
0.999999999999999888977697537484

但是:

s=0.1
s *= 10
print(s)
print('%.30f' % s)
1.0
1.000000000000000000000000000000

我想了解幕后发生的事情。

我知道十进制 0.1 的二进制表示永远不准确,可以通过以下方式验证:

print(0.1)
print('%.30f' % 0.1)
0.1
0.100000000000000005551115123126

因此,在一系列求和中,余数 5.55e-18 不断累加到变量中,并且它增长得非常快。

然而,当乘法时,我希望相同的余数也被乘以并且它会增长,但那并没有发生。这是为什么?在转换为二进制之前进行任何类型的优化?

它只与结果的四舍五入方式有关(在内部,二进制)。 0.1converts to

0.1000000000000000055511151231257827021181583404541015625

也就是

0.0001100110011001100110011001100110011001100110011001101in binary

Multiply that乘以 10(二进制为 1010)得到

1.000000000000000000000000000000000000000000000000000001

即55个有效位;四舍五入到 53 位等于 1.0.

加 0.1 十次,您将经历一系列舍入(您假设错误 "keeps adding up to the variable and very quickly it grows" 是错误的——为什么加 0.1 十次 小于 1.0 那么?)。如果您在每次迭代后打印完整的十进制值,您应该会看到

0.1000000000000000055511151231257827021181583404541015625
0.200000000000000011102230246251565404236316680908203125
0.3000000000000000444089209850062616169452667236328125
0.40000000000000002220446049250313080847263336181640625
0.5
0.59999999999999997779553950749686919152736663818359375
0.6999999999999999555910790149937383830547332763671875
0.79999999999999993338661852249060757458209991455078125
0.899999999999999911182158029987476766109466552734375
0.99999999999999988897769753748434595763683319091796875

例如,看看 0.5 和 0.6 之间发生了什么。添加 0.5 和 0.1

的内部二进制值

0.1 + 0.0001100110011001100110011001100110011001100110011001101

答案是

0.1001100110011001100110011001100110011001100110011001101

即55位;四舍五入为 53 位

0.10011001100110011001100110011001100110011001100110011

十进制是

0.59999999999999997779553950749686919152736663818359375

小于 0.6,尽管您可能期望它更大。