Java踏坑記シリーズのBigDecimal

3199 ワード

java.mathパケットには、BigInteger,BigDecimalクラスなどの高精度な計算を行うための大きな数字の操作クラスが提供される.通常、我々の開発で最も多く使用されているfloatとdoubleは一般的な科学とエンジニアリング計算にしか適用できません.通貨のような正確な計算を行うには、floatとdoubleを使用すると精度が失われます.そのため、大きな数字を精密に計算するためのクラスBigDecimalは少なくありません.したがって、BigDecimalは、16ビットを超える有効ビットの数を正確に演算するために商業計算シーンに適している.しかし、BigDecimalの使用はfloatやdoubleのように、不適切な使用による結果がより深刻ではありません.次に、私たちのプロジェクトでBigDecimalを踏んだ穴を見てみましょう.
一. BigDecimalの初期化精度喪失の問題
まず、次のコードの実行結果を見てみましょう.
BigDecimal bd1 = new BigDecimal(0.1);
System.out.println("bd1="+bd1);
BigDecimal bd2 = new BigDecimal("0.1");
System.out.println("bd2="+bd2);
BigDecimal bd3 = BigDecimal.valueOf(0.1);
System.out.println("bd3="+bd3);

出力結果:
bd1=0.1000000000000000055511151231257827021181583404541015625
bd2=0.1
bd3=0.1

floatまたはdoubleタイプの回転Bigdecimalの場合、new BigDecimal()の回転を使用しないでください.valueOf()の方法またはnew BigDecimal("")のstringを使用しないと、精度の問題が発生する可能性があります.
『Effective Java』という本には、
正確な答えが必要な場合はfloatとdoubleは避けてください
floatとdoubleはバイナリ浮動小数点演算を実行するため、バイナリは小数点を正確に表すことができない場合があります.例えば、10進数が正確に表すことができない1/3(1/3=0.3333...)つまり、バイナリが小数点を表す場合、1/(2^n)の和を表すことができる任意の組み合わせを表すことができます.例えば、
0.5は1/2になることを表すことができるので、表すことができます.
0.75は、1/2+1/(2^2)となることを表すことができるので、表すこともできる.
0.875は、1/2+1/(2^2)+1/(2^3)となることを表すことができるので、表すこともできる.
しかし、0.1は1/(2^n)の和となる形を表すことができないため、正確に表すことはできない.
System.out.println(0.5*3);
System.out.println(0.1*3);

この2行のコードをローカルで実行することができ、出力結果を見ると、なぜバイナリが0.1を表すことができないのに0.5を表すことができるのかがわかります.したがって、実際にはBigDecimalの問題ではなく、BigDecimalは正確な演算を満たすために存在し、問題は0.1自体が不正確な値であり、これは実際にはBigDecimalとは関係ないが、使用する際に注意しなければならない.
二.BigDecimalは除算演算を行う際に精度を設定しなければならない.そうしないと、除算が尽きない場合に異常を投げ出す
次のコードの実行結果を続行します.
BigDecimal bd4 = new BigDecimal("10");
BigDecimal bd5 = new BigDecimal("3");
System.out.println(bd4.divide(bd5));

出力結果:
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1690)
at BigDecimalTest.main(BigDecimalTest.java:38)

小数点以下の桁数を以下のように設定し、それを超えると四捨五入または上/下に整列または直接捨てる必要があります.
System.out.println(bd4.divide(bd5,2,BigDecimal.ROUND_DOWN));

2番目のパラメータは小数点以下の桁数を表し、3番目のパラメータは超過した桁数を直接捨てることを表す(もちろん四捨五入、上向き整列なども設定可能)
三.BigDecimalのequalsメソッドを使用してサイズを比較しないでください.そうしないと、精度の問題で比較結果と予想の不一致を引き起こす可能性があります.
BigDecimal bd1 = new BigDecimal("0");
BigDecimal bd2 = new BigDecimal("0.0");
System.out.println(bd1.equals(bd2));
System.out.println(bd1.[compareTo](http://javakk.com/tag/compareto "       compareTo    ")(bd2) == 0)

出力結果:
equals:false
compareTo:true

もしあなたのBigDecimal値に小数があると確定できない場合は、compareToを使ったほうがいいです.
出典:http://javakk.com/21.html