中安拓也のブログ

中安拓也がプログラミングについて書くブログ

【Java】BigDecimal型を使う

はじめに

仕事で金利に関するロジックを書いた時に、JavaのBigDecimal型を使ったので、BigDecimal型の使い方についてメモすることにしました。

なぜBigDecimal型を使うのか

BigDecimalには下記のような特徴があるため、誤差・意図しない値の丸めが発生することが許されない金額の計算のようなケースでは、float, double型ではなくBigDecimalの使用が推奨されます。

1. float, double型と違って少数の計算で誤差が発生しない

BigDecimalと同様に少数を表現する型として、float型とdouble型があります。float型とdouble型は、値を2進数で保持する関係上、循環小数としてしか表現できない10進数の少数が存在するため、少数を計算する過程で値を丸める必要があり、その過程で計算結果に誤差が発生してしまいます。 その反面、BigDecimal型では、値を「整数 * 10の何乗」という形式で保存しているため、10進数の有限小数のすべてを誤差なく表現することができます。

2. 値の丸めかたを指定することができる

BigDecimal型では、値の計算をすべてメソッド経由で実施するため、値の丸めかたを指定することができます。

// 1. float, double型では、少数の計算で誤差が発生するケースがある
double doubleAnswer = 3.3 / 1.1;
System.out.println("double: " + doubleAnswer); // double: 2.9999999999999996

BigDecimal bigDecimalAnswer = BigDecimal.valueOf(3.3).divide(BigDecimal.valueOf(1.1));
System.out.println("bigDecimal: " + bigDecimalAnswer); // bigDecimal: 3

// 2. 値の丸めかたを指定することができる
double doubleAnswer2 = 10.0 / 3.0;
System.out.println("double: " + doubleAnswer2); // double: 3.3333333333333335

BigDecimal bigDecimalAnswer2 = BigDecimal.valueOf(10.0).divide(BigDecimal.valueOf(3.0), 2, RoundingMode.HALF_UP);
System.out.println("bigDecimal: " + bigDecimalAnswer2); // bigDecimal: 3.33

BigDecimalの生成

数値からBigDecimalを生成するときは、valueOfメソッドを使用し、文字列から生成する場合は、コンストラクタを使用します。

BigDecimal value1 = BigDecimal.valueOf(1234.56);// 数値からの生成
BigDecimal value2 = new BigDecimal("1234.56");// 文字列から生成

BigDecimalの定数

0, 1, 10は定数として定義されています。

BigDecimal.ZERO
BigDecimal.ONE
BigDecimal.TEN

値の比較

equalsメソッド使用した場合、値のscaleが一致していないと、値が同一でもfalseが出力されてしまいます。

System.out.println(BigDecimal.valueOf(0.0).equals(BigDecimal.valueOf(0))); // 値のscaleが異なるため、falseになってしまう
System.out.println(BigDecimal.valueOf(0).equals(BigDecimal.valueOf(0))); // true

そのため、BigDecimalで値の比較を行う場合は、compareToメソッドを使用しましょう。compareToメソッドでは値は等しいがscaleが異なるBigDecimalオブジェクトを等しいとみなします。(例えば、2.0と2.00は等しいとみなされる)

BigDecimal one = BigDecimal.valueOf(1.00);
System.out.println(one.compareTo(BigDecimal.ZERO)); // 1
System.out.println(one.compareTo(BigDecimal.ONE)); // 0
System.out.println(one.compareTo(BigDecimal.TEN)); // -1

compareToメソッドでは、数値がパラメーターの値よりも小さい場合は、-1、等しい場合は0、大きい場合は1を返します。

加算

BigDecimal three = BigDecimal.valueOf(3.00);
BigDecimal four = BigDecimal.valueOf(4.0);

System.out.println(three.add(four)); // 7.0

減算

BigDecimal three = BigDecimal.valueOf(3.00);
BigDecimal four = BigDecimal.valueOf(4.0);

System.out.println(three.subtract(four)); // -1.0

乗算

BigDecimal three = BigDecimal.valueOf(3.00);
BigDecimal four = BigDecimal.valueOf(4.0);

System.out.println(three.multiply(four)); // 12.00

除算

scaleを指定せずに、divideメソッドを使うこともできますが、割り切れないときにArithmeticExceptionが発生するのでscale指定版を使用するほうが無難です。

BigDecimal three = BigDecimal.valueOf(3.00);
BigDecimal four = BigDecimal.valueOf(4.0);

System.out.println(three.divide(four, 3, RoundingMode.HALF_UP)); // 0.750

参考サイト

BigDecimal (Java Platform SE 8)

【Java】BigDecimalをちゃんと使う~2018~ - Qiita

BigDecimalの使い方 | Java好き

なぜBigDecimalを使わなければならないのか | Java好き

Java9以降のBigDecimalの小数点切り上げ・切り捨て・四捨五入-スケ郎のお話