浮点数的算术运算得出了意外的结果

3

我知道二进制表示法无法精确地表示浮点数(同时也了解为什么 0.1 + 0.2 == 0.3 是不正确的)。现在,当我尝试使用不同的浮点算术进行实验时,我遇到了一个问题:

通过观察十进制数字转换为二进制格式的方式,我得出结论:具有相同小数部分的两个数字在十进制表示法中(例如:0.3 和 1.3),一旦转换为二进制形式,它们将具有相同的小数部分。

为了测试这个结论,我在 Python 中尝试了以下代码:

print(f"{0.3:.20f}")
print(f"{1.3:.20f}")

0.29999999999999998890
1.30000000000000004441

我不理解为什么小数部分会出现不同,因此为了更好地了解情况,我尝试了这个方法:

  print(f"{0.3:.20f}")
  print(f"{1 + 0.3:.20f}")
  print(f"{1.3:.20f}")

  0.29999999999999998890
  1.30000000000000004441
  1.30000000000000004441

问题:由于 1 不是一个近似数(因为我们可以用二进制的精确形式 2^0 来表示 1),所以为什么加 1 会改变数字的小数部分呢?

此外,当我们从 1.3 中减去 1 时,为什么结果值不等于 0.3?

print(f"{1.3:.20f}")
print(f"{1.3 - 1:.20f}")
print(f"{0.3:.20f}")

1.30000000000000004441
0.30000000000000004441
0.29999999999999998890

我的整个问题可以用以下并置来概括:

print(1 + .3 == 1.3)
print(0.3 == 1.3 -1)

True
False

3
@KlausD. 我不认为这是重复问题。原帖作者似乎理解二进制、浮点数表示法以及它们的限制。他们询问的是关于这个特定现象的问题。 - juanpa.arrivillaga
2
关于“为什么加1会改变数字的小数部分?”的问题,这与四舍五入有关。 - njuffa
2
这是因为使用了相应的标准,如IEEE_754所描述的那样,由于不同的舍入截止值而导致的。换句话说,有一个将分数转换为四舍五入小数的过程,该过程在具有不同整数部分但相同小数部分的分数上不一致,正如您在1.3和0.3中注意到的那样,x.3中的.3根据x的不同而被舍入不同。 - Vepir
2
@j1-lee 浮点数计算的舍入操作是否导致有限精度浮点运算结果与天真期望的结果不同,取决于操作数的具体情况,例如它们的相对大小(有关具体示例,请参见Sterbenz引理、减法抵消)。有时结果与天真期望相匹配,有时则不是。为了掌握这一点,我建议手动模拟所涉及的二进制算术运算,所有问题都会变得清晰明了。 - njuffa
4
这是因为 njuffa 在他们上面的评论中解释了的原因:“有时结果符合天真的期望,有时不符合。” 浮点数的算术运算与你期望的天真算术运算不同。 您可以使用 ecs.umass.edu/ece/koren/arith/simulator/FPAdd/ 进行加减运算,h-schmidt.net/FloatConverter/IEEE754.html 进行转换。 实际上,我得到的结果是 0.3 + 1.0 == 1.3, 1.3 - 1.0 != 0.3 - Vepir
显示剩余8条评论
1个回答

0

准确地表示浮点数

这些浮点数占用固定的空间,即64位。这允许精确编码约264个不同的值。0.3、1.3不在该集合中。

每个有限编码值都是2的幂次方整数。因此,0.3、1.3无法被准确保存,因为它们不能被缩放为“2的幂次方整数”。相反,使用附近的值:

5404319552844595*pow(2,-54): 0.299999999999999988897... (closest)
                             0.3                        (not encodable)
5404319552844596*pow(2,-54): 0.300000000000000044408... (next closest)

5854679515581644*pow(2,-52): 1.299999999999999822364... (next closest) 
                             1.3                        (not encodable)
5854679515581645*pow(2,-52): 1.300000000000000044408... (closest)

0.3 == 1.3 -1 更好地理解为 0.299999999999999988897... == 1.300000000000000044408... - 1.0

从 1.300000000000000044408... 中减去 1.0 是精确的:0.300000000000000044408...,并且显然不等于 0.299999999999999988897...。


当我尝试 1.3 == 1 + 0.3 时,结果为真,这让我感到困惑。这就像 1.300000000000000044408... = 1.0 + 0.299999999999999988897...。和 1.299999999999999988897... 的总和不能表示为浮点数,因为在 integer * power-of-2 中编码整数使用的位数是有限的。最接近可表示的值被用作总和。
5854679515581644   *pow(2,-52): 1.299999999999999822364 (next closest) 
5854679515581644.75*pow(2,-52): 1.299999999999999988897 (not encodable)
5854679515581645   *pow(2,-52): 1.300000000000000044408 (closer)

0.3 != 1.3 -1 对我来说是有意义的,但当我尝试 1.3 == 1 + 0.3 时,结果为真,这就让我感到困惑。 - BOT_bkcd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接