私は古い試験問題 (現在は大学 1 年生) を調べていますが、次のfor
ループが終了するはずのときに終了しない理由を誰かがもう少し詳しく説明できるかどうか疑問に思っています。なぜこれが起こるのですか?丸め誤差か何かで 100.0 をスキップすることは理解していますが、なぜですか?
for(double i = 0.0; i != 100; i = i +0.1){
System.out.println(i);
}
私は古い試験問題 (現在は大学 1 年生) を調べていますが、次のfor
ループが終了するはずのときに終了しない理由を誰かがもう少し詳しく説明できるかどうか疑問に思っています。なぜこれが起こるのですか?丸め誤差か何かで 100.0 をスキップすることは理解していますが、なぜですか?
for(double i = 0.0; i != 100; i = i +0.1){
System.out.println(i);
}
1/3 を 10 進数で正確に表すことができないのと同様に、数値 0.1 を 2 進数で正確に表すことはできません。
0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1==1
これは、バイナリで次の理由によるものです。
0.1=(binary)0.00011001100110011001100110011001....... forever
ただし、double には無限の精度を含めることはできないため、1/3 を 0.3333333 に近似するように、2 進表現は 0.1 に近似する必要があります。
10進数では、次のことがわかります
1/3+1/3+1/3
=0.333+0.333+0.333
=0.999
これはまったく同じ問題です。私たち自身の 10 進法にも同じ問題があるため、これを浮動小数点数の弱点と見なすべきではありません (ただし、異なる数の場合、基数 3 のシステムを使用している人は、1/3 を表現するのに苦労していることを奇妙に感じるでしょう)。ただし、注意が必要な問題です。
Andrea Ligios が提供するライブ デモでは、これらのエラーが積み重なっていることを示しています。
まず、ダブルスについていくつか説明します。これは、理解を容易にするために、実際には 10 進法で行われます。
3 分の 1 の値を 10 進法で表現してみてください。0.3333333333333 が得られます....4 桁に丸める必要があるとしましょう。0.3333 が得られます。では、さらに 1/3 を追加しましょう。0.6666333333333.... を取得し、0.6666 に丸めます。さらに1/3を追加しましょう。1 ではなく、0.9999 を取得します。
基数 2 と 10 分の 1 でも同じことが起こります。0.1 10 で進み、 0.1 10は繰り返しバイナリ値 (0.1666666 など) であるため、そこに到達したときに 100 を逃すのに十分なエラーがあります。
1/2 は基数 10 で問題なく表すことができ、1/5 も同様に表すことができます。これは、分母の素因数が基数の因数のサブセットであるためです。これは、ベース 10 の 3 分の 1 またはベース 2 の 10 分の 1 には当てはまりません。