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 には特別な属性があるのではないかと考えていました。しかし、j を 1.13 から開始すると、その理論が覆されました。私が本当に得ているのは、これが一貫していないように見えるということです。言い換えれば、奇数の「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: 2.2204460492503131e-16 で定義されたイプシロン内に収まります。

つまり、計算はOKです、それよりも正確な数値だと思うのは表示です。それが私の最善の推測です。

ruby のソースコードの sprintf.c を見てみました。float を 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...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 ではこの動作は見られません。補足です。

于 2013-02-10T16:42:53.230 に答える