1

我有一个用 Ruby 编写的简单程序。代码是:

#!/usr/bin/ruby

# odd behavior here
j = 1.11
while j < 2
    print "iteration #{j}\n"
    j += 0.01
end

我在装有 Mountain Lion 的 MacBook Air 上运行它。我的 Ruby 版本是:

ruby 1.9.3p327 (2012-11-10 revision 37606) [x86_64-darwin11.4.2]

我看到的奇怪行为是:

iteration 1.1
iteration 1.11
iteration 1.12
iteration 1.1300000000000001
iteration 1.1400000000000001

现在,如果我将 j 更改为 1.13,我会得到以下结果(为简洁起见,我删除了一些输出):

iteration 1.13
iteration 1.14
iteration 1.15
...
iteration 1.36
iteration 1.37
iteration 1.3800000000000001
iteration 1.3900000000000001
iteration 1.4000000000000001
iteration 1.4100000000000001
iteration 1.4200000000000002

这里发生了什么?最初,我认为这可能与值如何存储在 j 中有关,并且 1.13 具有一些特殊属性。然而,从 1.13 开始的 j 颠覆了这一理论。真正让我感动的是,这似乎并不一致。换句话说,奇怪的“0000000000”在哪里发挥作用似乎有些武断(尽管我确定不是)。

更糟糕的是,如果我将“j < 2”更改为“j < 5”,我会得到更奇怪的行为。

...
iteration 4.85999999999994
iteration 4.86999999999994
iteration 4.8799999999999395
iteration 4.889999999999939
iteration 4.899999999999939
iteration 4.909999999999939
iteration 4.919999999999939
iteration 4.929999999999938
iteration 4.939999999999938
iteration 4.949999999999938
iteration 4.959999999999938
iteration 4.969999999999938
iteration 4.979999999999937
iteration 4.989999999999937
iteration 4.999999999999937

我试过用谷歌搜索,但老实说,我不确定从哪里开始。我发现了一些线程,人们看到了 to_d 的奇怪行为,但没有什么能具体回答我的问题。此外,我对 Ruby 的了解还不够,无法完全理解发生了什么。这一定是精确度和 Ruby 如何存储数字的问题,但我不知道该去哪里找。

非常感谢任何朝着正确方向的推动!谢谢!

4

1 回答 1

0

您看到的数字属于limits.h中定义的epsilon:2.2204460492503131e-16

换句话说,计算没问题,是显示器认为它是一个比它更精确的数字。这是我最好的猜测。

我查看了 ruby​​ 源代码中的 sprintf.c。它似乎将浮点数转换为 BigDecimal:

    switch (TYPE(val)) {
      case T_FLOAT:
        if (FIXABLE(RFLOAT_VALUE(val))) {
        val = LONG2FIX((long)RFLOAT_VALUE(val));
        goto bin_retry;
        }
        // THIS IS THE CONVERSION
        val = rb_dbl2big(RFLOAT_VALUE(val));
        if (FIXNUM_P(val)) goto bin_retry;
        bignum = 1;
        break;

这可能是奇怪显示的原因。但是,ruby double 是 C double AFAIK,因此计算应该与 c 中的相同,除了它们看起来很奇怪。

试试这个c程序,你就会明白我的意思。你应该得到与你的红宝石结果相匹配的无关紧要的 .000...1s:

#include <stdio.h>
int main(){
double j;

j = 1.1;

while (j < 2) {
    printf ( "iteration %1.16f\n", j);
    j += 0.01;
    }
}

顺便说一句,ruby 1.8.7 没有表现出这种行为。只是一个旁注。

于 2013-02-10T16:42:53.230 回答