【问题标题】:Arithmetic operations on floating point numbers giving unexpected results浮点数的算术运算给出了意想不到的结果
【发布时间】:2021-08-29 07:10:10
【问题描述】:

我知道使用二进制表示不可能精确地表示浮点数(我也理解为什么 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 不是一个近似数(因为可以将 1 以精确的二进制形式表示为 2^0),那么为什么加 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

【问题讨论】:

  • @KlausD。我不认为这是一个骗局。 OP 似乎了解什么是二进制浮点表示及其局限性。他们在询问这个特定的现象
  • 回复:“为什么加 1 会改变数字的小数部分?”舍入。
  • 这是由于使用了相应的标准(如 IEEE_754)导致的舍入截止值不同,如链接(重复)问题中所述。换句话说,有一个程序可以将分数转换为四舍五入的小数,这与具有不同整数部分但小数部分相同的分数不一致,正如您在 1.3 和 0.3 中注意到的那样,x.3 中的 .3 以不同的方式四舍五入取决于 x。
  • @j1-lee 舍入是否会导致有限精度浮点运算的结果与天真预期的结果不同取决于操作数的具体情况,例如,它们的相对大小(例如具体示例,请参见 Sterbenz 引理,减法抵消)。有时结果符合天真的期望,有时则不然。为了解决这个问题,我建议手动模拟有问题的二进制算术运算,一切都会变得清晰。
  • @Gravity 这是因为 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

标签: python floating-point


【解决方案1】:

精确表示一个浮点数

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

每个有限编码值都是一个integer * power-of-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.2999999999999999988897...。


当我尝试 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 时发现是真的,这让我感到困惑
猜你喜欢
  • 1970-01-01
  • 2023-03-05
  • 2020-07-19
  • 1970-01-01
  • 2013-01-31
  • 2018-09-04
  • 2019-05-19
  • 2017-01-05
相关资源
最近更新 更多