2013-11-20 117 views
3

所以浮点运算inexact但并不能完全解释什么是怎么回事:这个表达式为什么会导致浮点错误?

[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)。

我确定在引擎盖下有些微妙的变化,想知道发生了什么?

+1

用ruby 2.0.0p247和1.9.3p392 [x86_64-linux]转载。我喜欢“这个世界一直在转动的美丽”的部分:) :) – tessi

回答

5

这是不正确的浮点格式的尾数有足够的位来表示六十五分之二十六。 (“有效数字”是优选的术语。有效数是线性的。尾数对数。)

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

但是,不管你用什么整数为尾数或者你用什么指数,该格式可从来不会完全符合0.4。假设你有0.4 = ˚F•2 ē,其中˚FË是整数。然后2/5 = ˚F•2 ë,所以2 /(5 ˚F)= 2 ë,然后1 /(5 ˚F)= 2 ë - 1和5 f = 2 1- e。为了实现这一点,5必须是2的幂。它不是,所以你不能有。4 = f•2 e

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

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

a*26.0/65,a*26.0首先被评估。精确的数学结果四舍五入到最接近的可表示值,产生1.3000000000000000444089209850062616169452667236328125。然后这除以65.再次,答案是四舍五入的,产生0.0200000000000000004163336342344337026588618755340576171875。当Ruby打印这个值时,它显然决定它足够接近.02,它只能显示“.02”而不是完整的值。从某种意义上讲,这是合理的,如果将打印值.02转换回浮点数,则会再次获得实际值,即0.0200000000000000004163336342344337026588618755340576171875。所以“.02”在某种意义上是0.0200000000000000004163336342344337026588618755340576171875的好代表。

在您的替代表达式中,您有a*=26.0/65。在此,首先对26.0/65进行评估。这产生了0.40000000000000002220446049250313080847263336181640625。 这与第一个表达式不同,因为您已按不同的顺序执行操作,因此将舍入了不同的数字。可能发生的情况是,第一个表达式中的值被舍入,而这个不同的值,因为它发生的地方相对于可浮点表示的值,所以四舍五入。

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

+1

+1尊重!很好的解释。我有线索,但你把它弄清楚了。 –

+0

明智的解释。感谢您清除所有内容。 –

+0

请问您从哪里得到了所有这些结果的精确值?你是否使用计算或执行计算的工具?如果你使用红宝石,你是如何让它显示所有数字的?谢谢。 –

3

我想我在这里得到了什么。看看这个代码和操作顺序:

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。它评估右侧,然后将结果分配到作业的左侧。

现在,究竟是什么运算符* =呢?如果我们迫使运营a*(b/c)顺序的修改上面的代码显示你有那么a*=b/c所以在我认为Ruby的*=计算表达式的左侧,然后在右侧乘以它的引擎盖之后,它的结果将其分配到右侧。

在我的角度来看,这是发生了什么。 Ruby的解释器正在修改评估的执行方式,当然,因为我们正在处理非精确的浮点数,这可能对结果产生很大影响,Jon Skeet在他对这个问题的惊人回答中解释道:Why does changing the sum order returns a different result?

希望这有助于!

+0

好吧,很确定你是对的[63] pry(main)> 0.05 * 0.4 => 0.020000000000000004'但我仍然感到困惑,根据我对浮点运算的了解,这不应该发生,这两个表示应该有足够的位来表示数字?我记得总和顺序很重要,但是没有看到在这里如何适用? (再次感谢伟大的回答) –

+0

我可以看到你的观点。让我们等待一个拥有比我更高的浮点运算背景的人可以使这个变得简单。我只是假设没关系,因为使用浮点运算我总是准备最差的:) –

相关问题