我有一个用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开始,这个理论开始了。真正让我感到惊讶的是,这似乎并不一致。换句话说,奇怪的“0000000000”发挥作用似乎有点武断(虽然我确定不是)。
更糟糕的是,如果我将“j&lt; 2”改为“j&lt; 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如何存储数字,但我不确定在哪里看。
非常感谢正确方向的任何推动!谢谢!
答案 0 :(得分: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双AFAIK,因此计算应该与c中的相同,除非它们看起来很奇怪。
试试这个c程序,你会理解我的意思。你应该得到与你的红宝石结果相符的无关的.000 ...... 1:
#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没有表现出这种行为。只是旁注。