次の動作が古い問題であることは知っていますが、それでも理解できません。
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
?
どうすればこれを解決できますか?
あなたが実際に欲しいのは
_new BigDecimal("0.1")
.add(new BigDecimal("0.1"))
.add(new BigDecimal("0.1"));
_
new BigDecimal(double)
コンストラクターはdouble
のすべての不正確さを取得するため、_0.1
_と言った時点で、丸め誤差がすでに発生しています。 String
コンストラクターを使用すると、double
を経由することに関連する丸め誤差を回避できます。
最初に決して、BigDecimalのdoubleコンストラクターを使用しないでください。いくつかの状況では正しいことかもしれませんが、ほとんどの場合そうではありません
入力を制御できる場合は、すでに提案されているBigDecimalStringコンストラクターを使用してください。そうすれば、あなたはまさにあなたが望むものを手に入れることができます。すでにdoubleがある場合(結局発生する可能性があります)、doubleコンストラクターを使用せず、代わりに静的valueOf
メソッドを使用します。これには、少なくとも問題を軽減するdoubleの標準的な表現が得られるという優れた利点があります。結果は通常、はるかに直感的です。
これはJavaの問題ではなく、コンピュータ全般の問題です。中心的な問題は、10進形式(人間形式)から2進形式(コンピューター形式)への変換にあります。 10進形式の一部の数値は、無限の循環小数がないと2進形式で表現できません。
たとえば、10進数の0.3は0.01001100 ... 2進数ですが、コンピュータには数値を保存するための「スロット」(ビット)が限られているため、無限の表現全体を保存することはできません。たとえば、0.01001100110011001100のみを保存します。しかし、その10進数の数値は0.3ではなく、代わりに0.30000000000000004です。
あなたが抱えている問題は、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