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

Ruby 为什么这个表达式会导致浮点错误?,ruby,floating-point,Ruby,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这一行换成a*=26.0/65不是很好,我们少输入了一个字符!让我们看看结果如何 [48] pry(main)> a=0.05

所以浮点运算是,但这并不能完全解释这里发生了什么:

[46] pry(main)> a=0.05
=> 0.05
[47] pry(main)> a=a*26.0/65
=> 0.02
所以在这里,我们得到了我们所期望的,我们得到了正确的答案,世界继续美丽地转动。但是我们后来重写了这个函数,当我们这样做的时候,我们把
a=a*26.0/65
这一行换成
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中的每一个都应该足够长)


我肯定引擎盖下面有一些微妙的事情,我想知道发生了什么事?

我想我明白了。请查看此代码和操作顺序:

irb(main):001:0> a=0.05
=> 0.05
irb(main):002:0> b=26.0
=> 26.0
irb(main):003:0> c=65
=> 65
irb(main):004:0> a*b/c
=> 0.02
irb(main):005:0> a*(b/c)
=> 0.020000000000000004
这里,
a*b/c
是解释器应该如何评估表达式
a=a*26.0/65
。它计算右侧,然后将结果分配到分配的左侧

现在,操作符*=到底做什么?如果我们强制修改运算顺序,上面的代码显示了你得到的结果,然后在引擎盖下,我认为Ruby的
*=
计算表达式的左侧,然后乘以右侧,然后将其赋值给右侧

在我看来,这就是正在发生的事情。Ruby的解释器正在修改执行求值的方式,当然,因为我们处理的是非精确浮点数,这可能会对结果产生很大影响,Jon Skeet在对这个问题的惊人回答中解释道:


希望这有帮助

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

二进制浮点数的有效位是二进制整数。该整数根据指数进行缩放。要用二进制浮点表示26/65,也就是.4,我们必须将其表示为一个整数乘以二的幂。例如,对.4的近似值为1•2-1=.5。更好的近似值为3•2-3=.375。更好的是26•2-4=.40625

然而,无论您对有效位使用什么整数或使用什么指数,这种格式都不可能精确。假设你有.4=f•2e,其中f和e是整数。然后2/5=f•2e,so 2/(5f)=2e,然后1/(5f)=2e-1和5f=21-e。要实现这一点,5必须是2的幂。它不是,所以你不能有.4=f•2e

在IEEE-754 64位二进制浮点中,有效位有53位。因此,与.4最接近的可表示值为0.40000000000000002220446049250313080847263336181640625,等于3602879701896397•2-53

现在让我们看看你的计算。在
a=0.05
中,
0.05
被转换为浮点,产生0.0500000000000000277555756156289135105907917022705078125

a*26.0/65
中,首先计算
a*26.0
。精确的数学结果四舍五入到最接近的可表示值,产生1.300000000000000004440892909850062616169452667236328125。然后除以65。答案再次四舍五入,产生0.02000000000000000004163336342344337026588618755340576171875。当Ruby打印这个值时,它显然决定它与.02足够接近,它只能显示“.02”,而不是完整的值。这是合理的,因为如果将打印值.02转换回浮点值,则会再次得到实际值0.0200000000000000000416333636342344337026588618755340576171875。因此,.02在某种意义上是0.02000000000000000004163336342344337026588618755340576171875的一个很好的代表

在替代表达式中,您有
a*=26.0/65
。在这种情况下,首先评估
26.0/65
。这产生了0.40000000002220446049250313080847263336181640625这与第一个表达式不同,因为您以不同的顺序执行了操作,因此舍入了不同的数字。第一个表达式中的值可能是向下舍入的,而这个不同的值是向上舍入的,因为它发生在相对于浮点表示的值的land上


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

使用ruby 2.0.0p247和1.9.3p392[x86_64-linux]复制。我喜欢“世界一直在美丽地转动”这一部分:)好吧,我很确定你是对的,因为
[63]pry(main)>0.05*0.4=>0.0200000000000000004
,但我仍然感到困惑,从我对浮点算术的知识来看,这不应该发生,两种表示都应该有足够的位来表示数字?我记得求和顺序很重要,但我不明白这在这里是怎么应用的?(再次感谢你的回答)我明白你的意思。让我们拭目以待,如果有人在浮点运算方面比我有更多的背景知识,他能解决这个问题。我只是认为这没关系,因为对于浮点运算,我总是做好最坏的准备:)+1尊重!很好的解释。我有线索,但你说得很清楚。我能问你从哪里得到所有这些结果的精确值吗?您是否使用了计算或执行计算的工具?如果你使用ruby,你是如何让它显示所有数字的?谢谢