2012-11-27 8 views

答えて

21

あなたが国家としてではなく、すべての数字は、IEEE754で正確に表現することができます。このような

for (float value = 0.0f; value < 1.0f; value += 0.1f) 
    System.out.println(value); 

結果。これらの数字を印刷するためにJavaが使用するルールと組み合わせて、表示される内容に影響を与えます。

バックグラウンドについては、IEEE754の不正確さについて簡単に説明します。この特定のケースでは、0.1は正確に表すことができませんので、実際に使用された数字は0.100000001490116119384765625のようなものです。

これがなぜそうであるかの分析については、hereを参照してください。 「間違った」値を取得している理由は、そのエラー(0.000000001490116119384765625)が徐々に増加するためです。


0.1または0.2(または同様の数字は)常にショーそのエラーはJavaで印刷コードではなく、実際の値そのものに関係していない理由。

実際には0.1が予想より少し上回っていますが、それを印刷するコードではすべての数字が表示されるわけではありません。 10進数の後に50桁を渡すように書式文字列を設定すると、真の値が表示されます。

Javaがfloat(明示的な書式設定なし)を印刷する方法の詳細は、hereです。桁数に関連するビットは次のとおりです。

小数部を表現する数字は少なくとも1桁以上でなければなりません。それ以外にも、引数の値を一意に識別するために必要な桁数float型の隣接値から削除します。一例として

は、ここにこれがどのように機能するか、あなたを示すいくつかのコードです:

public class testprog { 
    public static void main (String s[]) { 
     float n; int i, x; 
     for (i = 0, n = 0.0f; i < 10; i++, n += 0.1f) { 
      System.out.print(String.format("%30.29f %08x ", 
       n, Float.floatToRawIntBits(n))); 
      System.out.println (n); 
     } 
    } 
} 

これの出力は次のとおりです。

0.00000000000000000000000000000 00000000 0.0 
0.10000000149011611938476562500 3dcccccd 0.1 
0.20000000298023223876953125000 3e4ccccd 0.2 
0.30000001192092895507812500000 3e99999a 0.3 
0.40000000596046447753906250000 3ecccccd 0.4 
0.50000000000000000000000000000 3f000000 0.5 
0.60000002384185791015625000000 3f19999a 0.6 
0.70000004768371582031250000000 3f333334 0.70000005 
0.80000007152557373046875000000 3f4cccce 0.8000001 
0.90000009536743164062500000000 3f666668 0.9000001 

最初の列はの本当値であり、フロート、IEEE754の制限からの不正確さを含む。

第2列は、浮動小数点値の32ビット整数表現(実際の整数値ではなくメモリ内での見方)で、低レベルのビット表現で値をチェックするのに便利です。

最後の列は、書式設定なしで番号を印刷するときに表示される列です。


今、あなたが表示されますいくつかのより多くのコードを、見て、両方の周囲の値の違いが印刷されているものを制御する方法を継続的に不正確な値を追加するの不正確はあなたに間違った番号、を与える方法:

public class testprog { 
    public static void outLines (float n) { 
     int i, val = Float.floatToRawIntBits(n); 
     for (i = -1; i < 2; i++) { 
      n = Float.intBitsToFloat(val+i); 
      System.out.print(String.format("%30.29f %.08f %08x ", 
       n, n, Float.floatToRawIntBits(n))); 
      System.out.println (n); 
     } 
     System.out.println(); 
    } 
    public static void main (String s[]) { 
     float n = 0.0f; 
     for (int i = 0; i < 6; i++) n += 0.1f; 
     outLines (n); n += 0.1f; 
     outLines (n); n += 0.1f; 
     outLines (n); n += 0.1f; 
     outLines (0.7f); 
    } 
} 

このコードは0.6まで取得するには0.1の継続的な追加を使用して、その隣接フロートの値を出力します。その出力は、次のとおり

0.59999996423721310000000000000 0.59999996 3f199999 0.59999996 
0.60000002384185790000000000000 0.60000002 3f19999a 0.6 
0.60000008344650270000000000000 0.60000008 3f19999b 0.6000001 

0.69999998807907100000000000000 0.69999999 3f333333 0.7 
0.70000004768371580000000000000 0.70000005 3f333334 0.70000005 
0.70000010728836060000000000000 0.70000011 3f333335 0.7000001 

0.80000001192092900000000000000 0.80000001 3f4ccccd 0.8 
0.80000007152557370000000000000 0.80000007 3f4cccce 0.8000001 
0.80000013113021850000000000000 0.80000013 3f4ccccf 0.80000013 

0.69999992847442630000000000000 0.69999993 3f333332 0.6999999 
0.69999998807907100000000000000 0.69999999 3f333333 0.7 
0.70000004768371580000000000000 0.70000005 3f333334 0.70000005 

見て最初は、最終列は上述のJava印刷仕様に従って周囲線(からそれを区別するために、各ブロックの中央線に十分な小数桁を有することです以前)。例えば

あなたが唯一の小数点以下3桁を持っていた場合、あなたは0.60.6000001(隣接するビットパターン0x3f19999a0x3f19999b)を区別することができないであろう。それで、必要なだけ印刷します。

もう1つ目のことは、2番目のブロックの0.7の値がではなく、0.7であることです。むしろ、にもかかわらず、その数値に(さらに前の行で)さらに近いビットパターンがあるという事実にもかかわらず、0.70000005です。

これは、0.1を追加したことによる誤差の段階的な累積によって発生しています。最後のブロックから、0.1を連続的に追加するのではなく、0.7を直接使用しただけであれば、正しい値を得ることができます。

この場合、問題が発生するのは、です。あなたが0.70000005をプリントアウトしているという事実は、Javaが近似を十分に近似していないためではありません。それは、あなたが最初に0.7になったためです。

あなたが含まれているために上記のようにコードを変更した場合:

outLines (0.1f); 
outLines (0.2f); 
outLines (0.3f); 
outLines (0.4f); 
outLines (0.5f); 
outLines (0.6f); 
outLines (0.7f); 
outLines (0.8f); 
outLines (0.9f); 

あなたはそれが正しく、そのグループ内のすべての番号をプリントアウトすることができます見つけることができます。

関連する問題