浮点乘法的Ruby奇怪问题

时间:2012-07-12 04:43:09

标签: ruby-on-rails ruby

有没有人在ruby中解决这个问题:

我们说:a = 8.1999999

我们希望将其舍入2位小数,即8.20,然后乘以1,000,000,变为8,200,000

我们这样做;

(a.round(2) * 1000000).to_i

但我们得到的是8199999,为什么?

事情是,如果我们乘以1000,100000或10000000而不是1000000,我们得到了正确的结果。任何人都知道为什么?

我们正在使用ruby 1.9.2并尝试使用1.9.3。

谢谢!

3 个答案:

答案 0 :(得分:8)

每当你在计算中得到时髦的数字时使用bigdecimal

require 'bigdecimal'
a = BigDecimal(8.1999999.to_s)
(a.round(2) * 1000000).to_i

答案 1 :(得分:3)

因为a.round(2)返回浮点数,因此计算不完美。

要获得正确的结果,请尝试以下操作:(10 * a).round.to_i * 100000

答案 2 :(得分:0)

在某种意义上,您的初始舍入 正在工作, 1 。问题是8.2没有精确的内部表示。如果您只需将8.2输入到irb中或显示#round(2)方法调用的结果,那么看起来就像您有8.2但不是。实际存储的数字略小于8.2。

您最终会被输出舍入逻辑的默认值所击败。一旦内部略小于8.2位被加倍,错误就会转移到数字的整数部分,除非你要求,否则这部分不会被舍入。你可以这样做:(a * 1000000).round

问题是我们用十进制编写数字但是将它们存储在二进制中。这适用于整数;但它的分数很差。

事实上,我们编写的大多数小数部分无法准确表示。

每个机器分数是x / 2 n 形式的有理数。现在,常数是十进制的,每个十进制常数是x /(2 n * 5 m )形式的有理数。 5 m 数字是奇数,因此对于它们中的任何一个都没有2 n 因子。只有当 m == 0 时,分数的二进制和十进制扩展都有一个有限的表示。因此,1.25是准确的,因为它是5 /(2 2 * 5 0 )但0.1不是因为它是1 /(2 0 * 5 1 )。实际上,在1.01 .. 1.99系列中,只有3个数字可以表示:1.25,1.50和1.75。

因为8.2没有精确的表示,所以它永远以二进制形式重复,从未完全相加到8.2。它继续无限为1100110011 ...


1。但请注意,您可能需要a.round(1)而不是2. #round的参数是您想要的分数位数,而不是重要数数字。在这种情况下,结果是相同的,并不重要。