BigDecimalとは?
BigDecimalの概要と必要性
BigDecimal
は、Javaで高精度の小数計算を行うためのクラスです。double
や float
を使うと、2進数の丸め誤差により10進数の計算結果が正確に出ないことがあります。
特に、お金の計算や精密な計測値の計算では誤差が許されないため、BigDecimal
を使用するのが一般的です
doubleやfloatとの違い
項目 | float / double | BigDecimal |
---|---|---|
精度 | 有限桁(誤差が発生する) | 任意の精度を保持できる |
計算の誤差 | 発生する(2進数の丸め誤差) | 発生しない(10進数の精度維持) |
速度 | 高速 | 遅い(計算コストが高い) |
主な用途 | 科学計算、ゲーム、画像処理 | 金融計算、精密計算 |
BigDecimalの初期化と基本操作
BigDecimal の生成方法(初期化)
String、double、int からの生成する方法です。
1 2 3 4 5 |
BigDecimal value1 = new BigDecimal("0.1"); // 文字列から生成 BigDecimal value2 = BigDecimal.valueOf(0.1d); // double から生成 BigDecimal value3 = BigDecimal.valueOf(123); // int から生成 |
valueOfメソッドの引数にはlongとdoubleのものしかないのですが、intはlongのメソッドとして扱われるので問題ありません。
null の扱いと注意点
BigDecimal
の変数に null
が入っていると NullPointerException
が発生するため適切な初期値を設定することが推奨されます。
NullPointerExceptionが発生する
1 2 3 4 |
String value = null; new BigDecimal(value); // NullPointerExceptionが発生する |
何らかの初期値を入れて回避する
1 2 3 4 5 6 |
String value = null; if (value == null) { // 何らかの初期化処理 } |
BigDecimalの計算
基本的な四則演算(足し算・引き算・掛け算・割り算)
1 2 3 4 5 6 7 8 |
BigDecimal x = BigDecimal.valueOf(10.5); BigDecimal y = BigDecimal.valueOf((2.3)); BigDecimal sum = x.add(y); // 足し算 BigDecimal diff = x.subtract(y); // 引き算 BigDecimal product = x.multiply(y); // 掛け算 BigDecimal quotient = x.divide(y, RoundingMode.HALF_UP); // 割り算(丸めが必要) |
計算時の優先順位
通常の数学と同じ優先順位で演算は行われません。なので計算順序には注意してください。
例えば以下のように、加算してから乗算すると加算が先に行われます。
1 2 3 4 5 6 7 |
BigDecimal x = BigDecimal.valueOf(10); BigDecimal y = BigDecimal.valueOf((2)); BigDecimal result = x.add(y).multiply(x); // 120 // ようするに、(10 + 2) * 10 となります |
剰余(余り)の計算
余りの算出は remainder
メソッドを使います。
除算と余りの結果を両方とも取得したい場合は、divideAndRemainder
メソッドを使います。その場合、戻り値はBigDecimalの配列になることに注意してください。
1 2 3 4 5 6 7 8 9 |
BigDecimal x = BigDecimal.valueOf(10); BigDecimal y = BigDecimal.valueOf((7)); BigDecimal remainder = x.remainder(y); // 3 BigDecimal[] divideAndRemainder = x.divideAndRemainder(y); // [1, 3] |
BigDecimalの比較方法
compareTo メソッド
比較は基本的にcompareTo
メソッドを使います。
1 2 3 |
int result = x.compareTo(y); |
戻り値のint値で比較結果を判定します。
- 0:同値 (x == y)
- -1:左辺が小さい (x < y)
- 1:左辺が大きい (x > y)
equals メソッドを使わない理由
equals
はスケール(小数点以下の桁数)が異なると false
を返すため、値を比較する場合は compareTo
を使うのが一般的です。
1 2 3 4 5 6 7 |
BigDecimal x = BigDecimal.valueOf(10.0); BigDecimal y = BigDecimal.valueOf((10)); System.out.println(x.equals(y)); // false System.out.println(x.compareTo(y) == 0); // true |
比較演算子 (==, >, < など) が使えない理由
BigDecimal
はオブジェクトなので、==
を使うと参照比較になってしまい、期待通りの動作をしません。compareTo
を使用することが推奨されます。
また>, <
はそもそも使えません。
BigDecimalの丸め処理(四捨五入)
setScale を使った丸め方
setScale
メソッドを使うことで小数点の桁数を指定できます。
通常は小数点の桁数を増やすために使いますが、切り捨てる桁全てが0である場合に限り桁数を減らすこともできます。
桁数を減らすことができる場合
1 2 3 4 5 |
BigDecimal x = BigDecimal.valueOf(10.123450000); System.out.println(x.setScale(5)); // 10.12345 |
桁数を減らせない場合はRoundingModeを指定する
もしRoundingMode
を指定せずに切り捨てる桁に0以外が含まれていた場合、ArithmeticException
がthrowされます。
1 2 3 4 5 6 7 |
BigDecimal y = BigDecimal.valueOf((10.123456789)); System.out.println(y.setScale(5, RoundingMode.HALF_UP)); // 10.12346 System.out.println(y.setScale(5)); // ArithmeticException |
RoundingMode の種類と選び方
通常使うのは以下の3つくらいです。
RoundingMode.HALF_UP
: 四捨五入RoundingMode.DOWN
: 切り捨てRoundingMode.UP
: 切り上げ
他のモードはこちらで確認できます。
BigDecimalの便利メソッド
小数点以下の桁数を取得
scale
メソッドを使います。
1 2 3 4 5 |
BigDecimal x = BigDecimal.valueOf(10.12345); int scale = x.scale(); // 5 |
最大値・最小値を比較して取得
max, min
メソッドを使います。
1 2 3 4 |
BigDecimal max = x.max(y); BigDecimal min = x.min(y); |
絶対値を取得
abs
メソッドを使います。
1 2 3 |
BigDecimal abs = x.abs(); |
計算時の注意点
String、int、longからであれば正確に制度を維持できますが、doubleとfloatには注意が必要です。
問題があるケース
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
String s = "1.0000000000000003"; System.out.println(new BigDecimal(s)); // 1.0000000000000003 double d = 1.0000000000000003; System.out.println(d); // 1.0000000000000002 System.out.println(BigDecimal.valueOf(d)); // 1.0000000000000002 float f = 1.0003f; System.out.println(f); // 1.0003 System.out.println(BigDecimal.valueOf(f)); // 1.0003000497817993 |
- Stringでは問題無い桁数でも、doubleだと値が変ってしまうので、BigDecimalに格納する時点でおかしくなります。
- floatも誤差が発生してしまいます。
対処法
- BigDecimalに格納する前にdoubleを使わない。また、BigDecimalからdoubleに格納するのは極力避ける。
- doubleをBigDecimalにどうしても格納する必要がある場合、しかるべき丸め仕様を決めて行う。
まとめ
BigDecimal
は、高精度な計算が必要な場合に不可欠なクラスです。特に、金融計算など誤差が許されない場面ではdouble
やfloat
ではなくBigDecimal
を使用することが推奨されます。BigDecimal
とdouble
の変換には精度誤差が発生する可能性があるので、変換が必要な場合の取り扱いには注意すること。
コメント