JavaでBigDecimal使うときに気をつけること
BIgDecimal周りの概念忘れがちなこと
精度とスケールの違い
精度
全体の有効桁数
code: 精度を指定した場合.java
jshell> new BigDecimal("10").divide(new BigDecimal("3"), MathContext.DECIMAL32); // DECIMAL32は精度7桁
// 全体が7桁
$7 ==> 3.333333
スケール
小数点以下の有効桁数
code: スケールを指定した場合.java
jshell> new BigDecimal("10").divide(new BigDecimal("3"), 3, RoundingMode.HALF_EVEN);
// 少数点以下が3桁
$6 ==> 3.333
蛇足だがどちらも指定しない場合
code: していなし.java
jshell> new BigDecimal("10").divide(new BigDecimal("3"));
| 例外java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
| at BigDecimal.divide (BigDecimal.java:1722)
| at (#3:1)
気をつけることリスト
初期化はvalueOfで
doubleとかでインスタンス作らない
intとかで作って、割り算してインスタンス作るとかしない
Stringで作る
Effective Java参照
途中でdoubleに変換したりしない
doubleとかでインスタンス作らないと同じ
divideするときは scale + roundingMode or mathContextを入れる
計算結果が3.333....みたいな無限に続く結果(循環小数)になるとエラーになる
精度/スケールは気にすること
例えばdoubleの精度は15桁位あるのでそれくらいは設定したほうが良いとかの判断ができる
スケール(scale + roundingMode) か 精度(MathContext) はどっちを使うべきか?
一応金額計算とかで小数点以下の有効桁数を気にするときはスケールを使い、科学系の計算で全体の精度を気にするときはMathContextを使って精度を指定するのが良いという話は見かけた (とはいえケースバイケースでどっち入れるか判断になりそう)
10^nとかの掛け算/割り算するときは、scaleByPowerOfTenを使う
引数一つで済むしCPUにもやさしい (らしい)
解説
scaleByPowerOfTen(2)で * 100 (10の2乗)
scaleByPowerOfTen(-2)で / 100 (10の-2乗)
丸めモード指定するときは、RoundingModeを使う
divide(BigDecimal divisor, int roundingMode) とかはJava9からdeprecated
floatやdoubleはHALF_EVENと同様の方針で丸めに行くので、doubleとかを機械的に置き換えるときはこれをつかうのが良いと思う
doubleなどの浮動少数点型から置き換える場合、0除算などのエラー処理には気を配ること
doubleやfloatの計算は、非数として表現され基本的にRuntimeエラーにならない
1 / 0.0 -> Infinity
0 / 0.0 -> Nan
対して、BigDecimalではエラーになるので単純に置き換えただけでは振る舞いが変わる(今までエラーにならなかったものがエラーになったり)ことがある
BigDecimalの比較はcompareToを使う
BigDecimalのequals比較はスケールの値まで一致しないと同値にならない
compareToは値のみで比較するので、同値比較はcompareToを使うべき