浮点运算:误差的求和与乘法
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.0001100110011001100110011001100110011001100110011001101
in 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,尽管您可能期望它更大。
我试图理解这个简单示例背后的浮点运算。这两种代码在理论上在算术上是等价的,但显然一系列的加法比简单的乘法增加了更多的错误。
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.0001100110011001100110011001100110011001100110011001101
in 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,尽管您可能期望它更大。