Java

BigDecimalの使い方

More than 1 year has passed since last update.

小数を扱うコードを作成していて、色々と嵌ったのでそのときのメモ。

起きたこと

小数の値を除算する必要があったので、以下のようにBigDecimalを利用して除算するコードを書いた。

public class Divide {
    public static void main(String[] args) {
        BigDecimal num1 = new BigDecimal(1.1);
        BigDecimal num2 = new BigDecimal(100);

        System.out.println(num1.divide(num2));
    }
}

1.1÷100なので、結果は0.011になるはずだが、実行してみると「0.01100000000000000088817841970012523233890533447265625」と出力される。
なんでやねん、と思ったが使い方が悪かった。

何が起きていたのか

BigDecimalを生成するときに1.1を引数にしているが、こいつがすでに誤差を含んでいるらしい。
10進数小数を2進数小数で表すため、そもそも表現できない値がある。考えてみれば当たり前だ。。。
1.1はその表現できない値であるため、それをもとにBigDecimalを生成すると、その誤差まで保持してしまう。

どうすればいいのか

以下のように、doubleやfloatの値を利用しないようにする。

public class Divide {
    public static void main(String[] args) {
        BigDecimal num1 = new BigDecimal("1.1");
        BigDecimal num2 = new BigDecimal("100");

        System.out.println(num1.divide(num2));
    }
}

こうすれば、正確な計算が行われる。
それなら、BigDecimalにdoubleを引数とするコンストラクタは必要なのか・・・?

さらに嵌ったこと

以下のように割り切れない値を計算するとArithmeticExceptionが発生する。

public class Divide {
    public static void main(String[] args) {
        BigDecimal num1 = new BigDecimal("1.1");
        BigDecimal num2 = new BigDecimal("3");

        System.out.println(num1.divide(num2));
    }
}

BigDecimalのdivideメソッドはデフォルトだと割り切れない場合、例外を発生させるらしい。
そのため、以下のように桁数と丸め方を指定して利用する。

public class Divide {
    public static void main(String[] args) {
        BigDecimal num1 = new BigDecimal("1.1");
        BigDecimal num2 = new BigDecimal("3");

        System.out.println(num1.divide(num2,2,RoundingMode.FLOOR));
    }
}