【Java】doubleを使用した計算による誤差
既存アプリのバグに遭遇した際のメモ。
【問題】
次のような変数があった場合、それらの積はいくつになるだろうか。
double d1=558.3 int i1=100
期待値は言うまでもなく、55830.0でしょう。
しかし、55829.99999999999になっているケースがあったという話。
【原因】
結論から述べると下記2つ。
2つ目で多少ハマった。
1.doubleではなくBigDecimalを使用する(基本中の基本)
2.valが少数の場合、new BigDecimal(val)とすると数値誤差が発生しうる
【検証】
検証コードは次の通り。
package numtest; import java.math.BigDecimal; public class NumericalTest { public static void main(String[] args){ double d1=558.3; int i1=100; System.out.println("d1="+d1); System.out.println("i1="+i1); System.out.println("単純にdoubleの積だと数値誤差が発生する場合がある"); System.out.println("【バグ】d1*i1="+d1*i1); /** * */ System.out.println("BigDecimalを使用してみる"); BigDecimal d1bd = new BigDecimal(d1); BigDecimal i1bd = new BigDecimal(i1); BigDecimal r1bd = d1bd.multiply(i1bd); System.out.println("【バグ】r1bd="+r1bd.doubleValue()); /** * ①BigDecimalコンストラクタの引数にString.valueOf()を使用 * この表現は 1 つの引数を持つ Double.toString メソッドによって返されるものとまったく同じ。 */ d1bd = new BigDecimal(String.valueOf(d1)); r1bd = d1bd.multiply(i1bd); System.out.println("【修正案1】r1bd="+r1bd.doubleValue()); /** * ②BigDecimalコンストラクタの引数にDouble.toString()を使用 */ d1bd = new BigDecimal(Double.toString(d1)); r1bd = d1bd.multiply(i1bd); System.out.println("【修正案2】r1bd="+r1bd.doubleValue()); /** * ③BigDecimalコンストラクタを使用せずにBigDecimal.valueOf()を使用する・ * BigDecimalコンストラクタの引数に文字列を使用する場合と同じ */ d1bd = BigDecimal.valueOf(d1); r1bd = d1bd.multiply(i1bd); System.out.println("【修正案3】r1bd="+r1bd.doubleValue()); } }
検証結果は次の通り。
d1=558.3
i1=100
単純にdoubleの積だと数値誤差が発生する場合がある
【バグ】d1*i1=55829.99999999999
BigDecimalを使用してみる
【バグ】r1bd=55829.99999999999
【修正案1】r1bd=55830.0
【修正案2】r1bd=55830.0
【修正案3】r1bd=55830.0
下記ブログが非常に参考になりました。
◎参考ブログ
floatをもとにBigDecimalオブジェクトを作成する