先に結論
- doubleからBigDecimalクラスに変換する(せざるを得ない)場合には、
valueOf
メソッドを使いましょう。 - doubleの性質上、誤差の問題は避けられないので、充分注意しましょう。
前提環境
jdk 1.8.0_102
macOS Sierra 10.12.4
実装による結果の違い
Java
double d = 0.1;
// コード(1)
BigDecimal bd1 = new BigDecimal(d);
System.out.println(bd1.toPlainString());
// 0.1000000000000000055511151231257827021181583404541015625
// コード(2)
BigDecimal bd2 = new BigDecimal(Double.toString(d));
System.out.println(bd2.toPlainString());
// 0.1
// コード(3)
BigDecimal bd3 = BigDecimal.valueOf(d);
System.out.println(bd3.toPlainString());
// 0.1
// コード(4)
BigDecimal bd4 = new BigDecimal("1.0000000000000003");
System.out.println(bd4.toPlainString());
// 1.0000000000000003
// コード(5)
double d2 = 1.0000000000000003;
BigDecimal bd5 = BigDecimal.valueOf(d2);
System.out.println(bd5.toPlainString());
// 1.0000000000000002
- コード(1)のように、
BigDecimal(double)
コンストラクタでインスタンスを生成すると、多くの場合で誤差が発生し期待通りの結果を得られません。 - コード(4)のように、文字列から
BigDecimal(String)
コンストラクタを使用して生成すると、正確な値を取得できます。 - コード(2)の
BigDecimal(Double.toString(double))
、および、コード(3)のBigDecimal.valueOf(double)
メソッドでは、「ほぼ」期待通りの結果を得ることができます。(後述) - valueOfメソッドはキャッシュが効いてインスタンスが再利用されるケースがあります。すなわち扱う値によっては、コード(2)
BigDecimal(Double.toString(double))
よりコード(3)BigDecimal.valueOf(double)
の方が効率的な場合があります。
ということで、doubleからBigDecimalクラスに変換するには、valueOfメソッドが最善です。
数値ラッパークラスも含め、「数値オブジェクトの変換といえばvalueOf」と覚えてしまえば良いと思います。
ちなみに、BigDecimal(double)
を使用したことによるバグは、静的コード解析ツール FindBugs で検出することができます。
避けられない問題
Javaのdoubleのような浮動小数点数の性質として、すべての10進数(実数)を表現できません。
それに起因して、コード(5)のように、BigDecimalへの変換以前にdoubleに格納してしまった時点で誤差の発生が避けられない場合があります。
したがって、
- BigDecimalによる計算対象の値を、処理の過程でdoubleに格納しないことが原則
- doubleからBigDecimalへの変換が必要となった場合は、(しかるべき仕様を決めた上で)丸め処理を行う
というように、充分注意を払う必要があります。
参考