51

次のコードがあります。

Double i=17.31;
long j=(long) (i*100);
System.out.println(j);

O/P :1730 //Expected:1731

Double i=17.33;
long j=(long) (i*100);
System.out.println(j);

O/P :1732 //Expected:1733

Double i=17.32;
long j=(long) (i*100);
System.out.println(j);

O/P :1732 //Expected:1732{As expected}

Double i=15.33;
long j=(long) (i*100);
System.out.println(j);

O/P :1533 //Expected:1533{as Expected}

Google を試しましたが、理由が見つかりません。質問が些細なことでしたら申し訳ありません。

4

7 に答える 7

78

答えはどれも、なぜ 17.32違う行動をとったのかを扱っているようには見えません。

1. 発生理由

17.32とで見られる動作の違いは17.33 & 17.31、IEEE-754 の丸め規則によるものです。

適用される丸め規則: Java™ 仮想マシン仕様 §2.8.1 から

Java 仮想マシンの丸め操作では、IEEE 754 丸めモードが常に使用されます。不正確な結果は、最も近い表現可能な値に丸められ、同点は最下位ビットがゼロの値になります。これは IEEE 754 のデフォルト モードです。Java 仮想マシンは、浮動小数点の丸めモードを変更する手段を提供しません。


2. あなたのケース:

Double は: (1 符号ビット + 11 指数ビット + 52 小数ビット = 64 ビット)。以下の丸め後の内部表現:

             1 [63]      11 [62-52]           52 [51-00]
              Sign        Exponent             Fraction

17.31 -->    0 (+)       10000000011 (+4)     1.0001010011110101110000101000111101011100001010001111
17.32 -->    0 (+)       10000000011 (+4)     1.0001010100011110101110000101000111101011100001010010 //rounded up
17.33 -->    0 (+)       10000000011 (+4)     1.0001010101000111101011100001010001111010111000010100

3. 内部表現 (証明):

17.31: (仮数比較)

Actual:   1.00010100111101011100001010001111010111000010100011110...
Internal: 1.0001010011110101110000101000111101011100001010001111

17.32: (仮数比較)

Actual:   1.00010101000111101011100001010001111010111000010100011... 
Internal: 1.0001010100011110101110000101000111101011100001010010    //round-up!

17.33: (仮数比較)

Actual:   1.00010101010001111010111000010100011110101110000101000...
Internal: 1.0001010101000111101011100001010001111010111000010100

4. 10 進数に戻す変換:

17.31 ->  17.309999999999998721023075631819665431976318359375...
17.32 ->  17.32000000000000028421709430404007434844970703125... //(was rounded up)
17.33 ->  17.3299999999999982946974341757595539093017578125...

( IEEE-754 解析ツール)

5.ロングキャスト

編集: @Jeppe Stig Nielsen が言ったように、乗算ステップでより多くの要因があります。FP乗算 ( Reference ) ステップの結果は、最も近い方向への独自の丸めを行います。これにより、期待どおりの結果とそうでない結果が変わりますが、理由は上記とまったく同じです。

最後に、キャスト(long)により切り捨てが発生し、表示される結果が残ります。(1730, 1732, 1732)

プリミティブ変換のナローイング : Java™ 言語仕様 §5.1.3

浮動小数点数が無限大でない場合、浮動小数点値は整数値 V に丸められ、IEEE 754 round-toward-zero モードを使用してゼロに丸められます。

于 2012-11-20T10:28:15.030 に答える
22

double値は17.31ではなく、17.309999999999999として表されます。そのため、100を掛けると、1730.99999999999999999になります。値への変換後、ゼロに向かって切り捨てられますLongdoubleしたがって、1730を取得します。

于 2012-11-20T06:44:56.157 に答える
5

クトゥルフとsvzの答えは正しいです。double を 100 で乗算し、浮動小数点の丸めエラーを回避する場合は、各乗算後Math.round()に結果を最も近い値に丸めることができます。long

Double i=17.31;
long j=Math.round(i*100);
System.out.println(j);

非常に大きな (または負の) double を処理する場合、これには依然として浮動小数点エラーが発生します。double の絶対値が大きいほど、Java が表現できる次の double との差が大きくなります。ある時点の後、連続する double は整数以上離れており、従来の丸めでは差を滑らかにすることができなくなります。ただし、投稿した例では、これは機能するはずです。

于 2012-11-20T20:54:22.837 に答える
5

説明したように、これは浮動小数点精度が非常に小さいためです。

これは、次のように Math.round() コマンドを使用して解決できます。

long j=Math.round(i*100);

これにより、プログラムは浮動小数点計算を使用して継承される非常に小さなエラーを補正できます。デフォルト (ロング) のようにフロア演算を使用しません。

于 2012-11-20T10:56:24.940 に答える
4

それは内部表現と関係があります。最初のケースの i*100 を見ると、1730.9999999999998 であることがわかります。キャストは、ポイントの後の部分のみを削除します (切り捨てられます)。

于 2012-11-20T06:45:15.373 に答える
0

この種の長い変換を行うと、それは床です。17.31 は実際には 17.30999999999 になる可能性があるため、1731 ではなく 1730 になりました。

i = i * 100 を使用すると、 i.longValue() が問題を解決します。

于 2012-11-20T06:47:26.617 に答える