为什么这个表达式会导致浮点错误?

2024-05-04

所以浮点运算是inexact http://en.wikipedia.org/wiki/Floating_point但这并不能完全解释这里发生的事情:

[46] pry(main)> a=0.05
=> 0.05
[47] pry(main)> a=a*26.0/65
=> 0.02

所以我们得到了我们所期望的,我们得到了正确的答案,世界继续美丽地转动。但我们后来重写了这个函数,当我们这样做时,我们交换了行a=a*26.0/65 for a*=26.0/65是不是很好,我们少打了一个字!让我们看看这对我们来说效果如何?

[48] pry(main)> a=0.05
=> 0.05
[49] pry(main)> a*=26.0/65
=> 0.020000000000000004
[50] pry(main)> 26.0/65
=> 0.4

这表明a*=b与写作不一样a=a*b。这似乎不是正常的浮点舍入错误,因为这些数字都不应该舍入为浮点(尾数对于 26.0、26.0/65、65.0 中的每一个都应该足够长)

我确信幕后发生了一些微妙的事情,我想知道到底发生了什么?


浮点格式的有效数有足够的位来表示 26/65 是不正确的。 (“有效数”是首选术语。有效数是线性的。尾数是对数的。)

The significand of a binary floating-point number is a binary integer. This integer is scaled according to the exponent. To represent 26/65, which is .4, in binary floating-point, we must represent it as an integer multiplied by a power of two. For example, an approximation to .4 is 1•2-1 = .5. A better approximation is 3•2-3=.375. Better still is 26•2-4 = .40625.

However, no matter what integer you use for the significand or what exponent you use, this format can never be exactly .4. Suppose you had .4 = f•2e, where f and e are integers. Then 2/5 = f•2e, so 2/(5f) = 2e, and then 1/(5f) = 2e-1 and 5f = 21-e. For that to be true, 5 would have to be a power of two. It is not, so you cannot have .4 = f•2e.

In IEEE-754 64-bit binary floating-point, the significand has 53 bits. With this, the closest representable value to .4 is 0.40000000000000002220446049250313080847263336181640625, which equals 3602879701896397•2-53.

现在让我们看看您的计算。在a=0.05, 0.05转换为浮点数,生成 0.05000000000000000277555756156289135105907917022705078125。

In a*26.0/65, a*26.0首先被评估。精确的数学结果将四舍五入到最接近的可表示值,生成 1.3000000000000000444089209850062616169452667236328125。然后除以 65。同样,结果被四舍五入,得到 0.0200000000000000004163336342344337026588618755340576171875。当 Ruby 打印这个值时,它显然认为它足够接近 0.02,因此它只能显示“.02”而不是完整的值。这是合理的,因为如果将打印值 0.02 转换回浮点数,您将再次获得实际值 0.0200000000000000004163336342344337026588618755340576171875。因此,“.02”在某种意义上可以很好地代表 0.0200000000000000004163336342344337026588618755340576171875。

在你的替代表达中,你有a*=26.0/65。在这之中,26.0/65首先被评估。这会产生 0.40000000000000002220446049250313080847263336181640625。这与第一个表达式不同因为您以不同的顺序执行了运算,所以舍入了不同的数字。可能会发生第一个表达式中的值被向下舍入的情况,而这个不同的值由于它恰好相对于以浮点表示的值而发生的位置而被向上舍入。

然后将该值乘以a。这会产生 0.02000000000000000388578058618804789148271083831787109375。请注意,该值比第一个表达式的结果更远离 0.02。您的 Ruby 实现知道这一点,因此它确定打印“.02”不足以准确表示它。相反,它显示更多数字,显示 0.020000000000000004。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么这个表达式会导致浮点错误? 的相关文章

随机推荐