web-dev-qa-db-ja.com

Java BigDecimalの精度の問題

次の動作が古い問題であることは知っていますが、それでも理解できません。

System.out.println(0.1 + 0.1 + 0.1);    

または、BigDecimalを使用していても

System.out.println(new BigDecimal(0.1).doubleValue()
    + new BigDecimal(0.1).doubleValue()
    + new BigDecimal(0.1).doubleValue());

この結果がなぜであるか:0.30000000000000004 の代わりに: 0.3

どうすればこれを解決できますか?

14
Jefferson

あなたが実際に欲しいのは

_new BigDecimal("0.1")
 .add(new BigDecimal("0.1"))
 .add(new BigDecimal("0.1"));
_

new BigDecimal(double)コンストラクターはdoubleのすべての不正確さを取得するため、_0.1_と言った時点で、丸め誤差がすでに発生しています。 Stringコンストラクターを使用すると、doubleを経由することに関連する丸め誤差を回避できます。

27
Louis Wasserman

最初に決して、BigDecimalのdoubleコンストラクターを使用しないでください。いくつかの状況では正しいことかもしれませんが、ほとんどの場合そうではありません

入力を制御できる場合は、すでに提案されているBigDecimalStringコンストラクターを使用してください。そうすれば、あなたはまさにあなたが望むものを手に入れることができます。すでにdoubleがある場合(結局発生する可能性があります)、doubleコンストラクターを使用せず、代わりに静的valueOfメソッドを使用します。これには、少なくとも問題を軽減するdoubleの標準的な表現が得られるという優れた利点があります。結果は通常、はるかに直感的です。

9
Voo

これはJavaの問題ではなく、コンピュータ全般の問題です。中心的な問題は、10進形式(人間形式)から2進形式(コンピューター形式)への変換にあります。 10進形式の一部の数値は、無限の循環小数がないと2進形式で表現できません。

たとえば、10進数の0.3は0.01001100 ... 2進数ですが、コンピュータには数値を保存するための「スロット」(ビット)が限られているため、無限の表現全体を保存することはできません。たとえば、0.01001100110011001100のみを保存します。しかし、その10進数の数値は0.3ではなく、代わりに0.30000000000000004です。

4
Jakub Zaverka

あなたが抱えている問題は、0.1がわずかに高い数値で表されていることです。

System.out.println(new BigDecimal(0.1));

プリント

0.1000000000000000055511151231257827021181583404541015625

Double.toString()はこの表現エラーを考慮に入れているため、表示されません。

同様に、0.3は実際よりもわずかに低い値で表されます。

0.299999999999999988897769753748434595763683319091796875

0.1の表現値に3を掛けると、0.3の表現値は得られず、代わりに少し高い値が得られます。

0.3000000000000000166533453693773481063544750213623046875

これは、表現エラーだけでなく、操作によって発生する丸めエラーでもあります。これは、Double.toString()が修正する以上のものであるため、丸め誤差が表示されます。

ストーリーの教訓は、floatまたはdoubleを使用する場合も、ソリューションを適切に丸めます。

double d = 0.1 + 0.1 + 0.1;
System.out.println(d);
double d2 = (long)(d * 1e6 + 0.5) / 1e6; // round to 6 decimal places.
System.out.println(d2);

プリント

0.30000000000000004
0.3
3
Peter Lawrey