JavaのBigDecimalとは?小数計算・金額計算で注意すべきポイントを初心者向けに解説
Javaで小数を扱うときに、よく出てくるのが BigDecimal です。
例えば、金額や税金の計算をする場合、double や float ではなく BigDecimal を使うことがあります。
BigDecimal price = new BigDecimal("100.25");
BigDecimal taxRate = new BigDecimal("0.10");
BigDecimal tax = price.multiply(taxRate);
System.out.println(tax);
Javaには double や float という小数を扱う型もありますが、金額計算のように正確性が重要な場面では注意が必要です。
例えば、以下のように double で計算すると、想定と少し違う結果になることがあります。
double result = 0.1 + 0.2;
System.out.println(result);
実行結果は、次のようになることがあります。
0.30000000000000004
Javaを学び始めたばかりの頃は、以下のような点で迷いやすいです。
- BigDecimal は何のために使うのか
- double や float と何が違うのか
- new BigDecimal(0.1) と書いてよいのか
- BigDecimal の足し算、引き算、掛け算、割り算の書き方が分からない
- compareTo と equals の違いが分からない
- 実務ではどのような場面で使うべきか分からない
この記事では、Javaの BigDecimal について、基本的な考え方から実務でよく使う書き方までまとめます。
- 1. BigDecimalとは
- 2. doubleやfloatで小数誤差が出る理由
- 3. BigDecimalの基本的な作り方
- 4. BigDecimal.valueOfを使う方法
- 5. BigDecimalの足し算
- 6. BigDecimalの引き算
- 7. BigDecimalの掛け算
- 8. BigDecimalの割り算
- 9. RoundingModeとは
- 10. setScaleで小数点以下の桁数を指定する
- 11. BigDecimalはimmutable
- 12. BigDecimalの比較はcompareToを使う
- 13. BigDecimal.ZERO、ONE、TENを使う
- 14. 金額計算の例
- 15. doubleやfloatを使ってもよい場面
- 16. BigDecimalを使うべき場面
- 17. BigDecimalを使うときの注意点
- 18. まとめ
BigDecimalとは
BigDecimal とは、小数を正確に扱うためのクラスです。
Javaには小数を扱う型として double や float があります。
double value1 = 0.1;
float value2 = 0.1f;
しかし、double や float は内部的に2進数で小数を扱います。
そのため、10進数の小数を完全に正確に表現できない場合があります。
例えば、0.1 や 0.2 のような値でも、内部的には少し誤差を含むことがあります。
一方、BigDecimal を使うと、10進数の小数をより正確に扱うことができます。
BigDecimal value1 = new BigDecimal("0.1");
BigDecimal value2 = new BigDecimal("0.2");
BigDecimal result = value1.add(value2);
System.out.println(result);
実行結果は次のようになります。
0.3
このように、BigDecimal は金額や税金など、正確な計算が必要な場面でよく使われます。
doubleやfloatで小数誤差が出る理由
double や float は、小数を2進数で表現します。
コンピュータ内部では2進数が使われるため、これは自然な仕組みです。
しかし、私たちが普段使っている 0.1 や 0.2 のような10進数の小数は、2進数ではきれいに表現できないことがあります。
そのため、以下のようなコードを書くと、期待する 0.3 ではなく、わずかに誤差を含んだ値になることがあります。
double value1 = 0.1;
double value2 = 0.2;
double result = value1 + value2;
System.out.println(result);
実行結果の例です。
0.30000000000000004
この程度の誤差であれば、平均値やグラフ表示などでは問題にならないこともあります。
しかし、金額計算では1円単位のずれが問題になることがあります。
そのため、金額、税額、請求金額、精算金額などを扱う場合は BigDecimal を使うのが一般的です。
BigDecimalの基本的な作り方
BigDecimal を作るときは、基本的に文字列から生成するのがおすすめです。
BigDecimal value = new BigDecimal("0.1");
System.out.println(value);
実行結果は次のようになります。
0.1
注意したいのは、以下のように double から BigDecimal を作る書き方です。
BigDecimal value = new BigDecimal(0.1);
System.out.println(value);
この場合、すでに double の時点で誤差を含んでいる可能性があります。
実行結果は、次のようになることがあります。
0.1000000000000000055511151231257827021181583404541015625
そのため、BigDecimal を使う場合は、以下のように文字列で渡すのが安全です。
BigDecimal value = new BigDecimal("0.1");
BigDecimal.valueOfを使う方法
BigDecimal を作る方法として、BigDecimal.valueOf を使う方法もあります。
BigDecimal value = BigDecimal.valueOf(0.1);
System.out.println(value);
実行結果は次のようになります。
0.1
BigDecimal.valueOf は、double の値を文字列表現に変換してから BigDecimal を作るため、new BigDecimal(0.1) よりも扱いやすいです。
ただし、最初から値が決まっている場合は、文字列で生成する書き方が分かりやすいです。
BigDecimal price = new BigDecimal("100.25");
BigDecimal taxRate = new BigDecimal("0.10");
BigDecimalの足し算
BigDecimal は、int や double のように + 演算子で計算することはできません。
足し算をする場合は add メソッドを使います。
BigDecimal value1 = new BigDecimal("100");
BigDecimal value2 = new BigDecimal("50");
BigDecimal result = value1.add(value2);
System.out.println(result);
実行結果は次のようになります。
150
BigDecimalの引き算
引き算をする場合は subtract メソッドを使います。
BigDecimal value1 = new BigDecimal("100");
BigDecimal value2 = new BigDecimal("30");
BigDecimal result = value1.subtract(value2);
System.out.println(result);
実行結果は次のようになります。
70
BigDecimalの掛け算
掛け算をする場合は multiply メソッドを使います。
金額と税率を掛けるような処理でよく使います。
BigDecimal price = new BigDecimal("1000");
BigDecimal taxRate = new BigDecimal("0.10");
BigDecimal tax = price.multiply(taxRate);
System.out.println(tax);
実行結果は次のようになります。
100.00
この例では、1000円に10%の税率を掛けて、税額100円を計算しています。
BigDecimalの割り算
BigDecimal の割り算では、divide メソッドを使います。
BigDecimal value1 = new BigDecimal("10");
BigDecimal value2 = new BigDecimal("2");
BigDecimal result = value1.divide(value2);
System.out.println(result);
実行結果は次のようになります。
5
ただし、割り切れない計算では注意が必要です。
例えば、10 を 3 で割る場合です。
BigDecimal value1 = new BigDecimal("10");
BigDecimal value2 = new BigDecimal("3");
BigDecimal result = value1.divide(value2);
System.out.println(result);
このコードを実行すると、ArithmeticException が発生します。
java.lang.ArithmeticException: Non-terminating decimal expansion
これは、10 ÷ 3 の結果が 3.3333… と無限に続くためです。
BigDecimal では、割り切れない計算をするときに、小数点以下何桁まで求めるか、どのように丸めるかを指定する必要があります。
BigDecimal value1 = new BigDecimal("10");
BigDecimal value2 = new BigDecimal("3");
BigDecimal result = value1.divide(value2, 2, RoundingMode.HALF_UP);
System.out.println(result);
実行結果は次のようになります。
3.33
RoundingModeとは
RoundingMode は、端数をどのように丸めるかを指定するためのものです。
よく使われるのは HALF_UP です。
BigDecimal value = new BigDecimal("123.456");
BigDecimal result = value.setScale(2, RoundingMode.HALF_UP);
System.out.println(result);
実行結果は次のようになります。
123.46
HALF_UP は、一般的な四捨五入に近い丸め方です。
ただし、実務では業務ルールによって丸め方が決まっていることがあります。
例えば、消費税の端数を切り捨てる、四捨五入する、切り上げるなどのルールです。
- HALF_UP は四捨五入に近い丸め方
- DOWN は切り捨て
- UP は切り上げ
- 実務では業務ルールに合わせて選ぶ
setScaleで小数点以下の桁数を指定する
BigDecimal では、小数点以下の桁数を scale と呼びます。
例えば、123.45 の scale は 2 です。
BigDecimal value = new BigDecimal("123.45");
System.out.println(value.scale());
実行結果は次のようになります。
2
小数点以下の桁数をそろえたい場合は、setScale を使います。
BigDecimal value = new BigDecimal("123.4");
BigDecimal result = value.setScale(2, RoundingMode.HALF_UP);
System.out.println(result);
実行結果は次のようになります。
123.40
金額を小数点以下2桁で扱いたい場合などに使います。
BigDecimalはimmutable
BigDecimal は immutable なクラスです。
immutable とは、一度作成したオブジェクトの値が変更されないという意味です。
そのため、add や subtract などのメソッドを呼び出しても、元の BigDecimal の値は変更されません。
例えば、以下のコードを見てください。
BigDecimal value = new BigDecimal("100");
value.add(new BigDecimal("50"));
System.out.println(value);
実行結果は次のようになります。
100
150 になると思うかもしれませんが、実際には 100 のままです。
add メソッドは、計算結果を新しい BigDecimal として返します。
そのため、計算結果を変数で受け取る必要があります。
BigDecimal value = new BigDecimal("100");
value = value.add(new BigDecimal("50"));
System.out.println(value);
実行結果は次のようになります。
150
BigDecimalの比較はcompareToを使う
BigDecimal の比較では、equals と compareTo の違いに注意が必要です。
例えば、以下のコードを見てください。
BigDecimal value1 = new BigDecimal("1.0");
BigDecimal value2 = new BigDecimal("1.00");
System.out.println(value1.equals(value2));
System.out.println(value1.compareTo(value2));
実行結果は次のようになります。
false
0
equals は、数値だけでなく scale も比較します。
1.0 と 1.00 は、数値としては同じですが、小数点以下の桁数が違います。
そのため、equals では false になります。
一方、compareTo は数値として比較します。
compareTo の結果が 0 であれば、数値として等しいという意味です。
BigDecimal value1 = new BigDecimal("1.0");
BigDecimal value2 = new BigDecimal("1.00");
if (value1.compareTo(value2) == 0) {
System.out.println("数値として同じです");
}
BigDecimal を数値として比較する場合は、基本的に compareTo を使うと分かりやすいです。
BigDecimal.ZERO、ONE、TENを使う
BigDecimal には、よく使う値が定数として用意されています。
- BigDecimal.ZERO
- BigDecimal.ONE
- BigDecimal.TEN
例えば、0 と比較したい場合は、以下のように書けます。
BigDecimal amount = new BigDecimal("100");
if (amount.compareTo(BigDecimal.ZERO) > 0) {
System.out.println("0より大きいです");
}
毎回 new BigDecimal(“0") と書くよりも、BigDecimal.ZERO を使う方が分かりやすいです。
金額計算の例
BigDecimal は、金額計算でよく使われます。
例えば、商品の価格と税率から税込価格を計算する例です。
BigDecimal price = new BigDecimal("1000");
BigDecimal taxRate = new BigDecimal("0.10");
BigDecimal tax = price.multiply(taxRate);
BigDecimal total = price.add(tax);
System.out.println(total);
実行結果は次のようになります。
1100.00
実務では、税額の端数処理が必要になることもあります。
例えば、小数点以下を切り捨てる場合は、以下のように書けます。
BigDecimal price = new BigDecimal("999");
BigDecimal taxRate = new BigDecimal("0.10");
BigDecimal tax = price.multiply(taxRate)
.setScale(0, RoundingMode.DOWN);
BigDecimal total = price.add(tax);
System.out.println(tax);
System.out.println(total);
実行結果は次のようになります。
99
1098
端数を切り捨てるのか、四捨五入するのか、切り上げるのかは、システムの業務ルールに合わせる必要があります。
doubleやfloatを使ってもよい場面
小数を扱う場合でも、必ず BigDecimal を使う必要があるわけではありません。
厳密な10進数の計算結果が必要ない場合は、double や float を使っても問題ないことがあります。
例えば、以下のような値です。
- 平均値
- 割合
- 温度
- 距離
- 速度
- グラフ表示用の値
- ゲームの座標や角度
これらは、多少の誤差があっても実務上問題になりにくいことがあります。
例えば、平均値を計算する場合です。
double total = 253.0;
double count = 3.0;
double average = total / count;
System.out.println(average);
実行結果は次のようになります。
84.33333333333333
このような値は、画面表示のときに小数点以下2桁に丸めれば十分なことがあります。
double average = 84.33333333333333;
System.out.printf("%.2f%n", average);
実行結果は次のようになります。
84.33
BigDecimalを使うべき場面
BigDecimal を使うべきなのは、正確な10進数計算が必要な場面です。
代表的には、以下のようなケースです。
- 金額
- 税額
- 請求金額
- 給与
- ポイント残高
- 精算金額
- 単価と数量の計算
これらの値は、わずかな誤差でも業務上の問題につながる可能性があります。
そのため、金額や税金のような値は、基本的に BigDecimal を使うと考えると分かりやすいです。
BigDecimalを使うときの注意点
BigDecimal を使うときは、以下の点に注意するとよいです。
- new BigDecimal(0.1) は避ける
- 基本的には文字列から生成する
- 割り算では桁数と丸め方を指定する
- 計算結果は戻り値で受け取る
- 数値比較では compareTo を使う
- 端数処理は業務ルールに合わせる
特に、new BigDecimal(0.1) と equals の比較は、初心者がつまずきやすいポイントです。
実務では、BigDecimal を使っているから安心というわけではなく、生成方法、比較方法、丸め方法まで意識することが大切です。
まとめ
BigDecimal は、Javaで小数を正確に扱うためのクラスです。
double や float は小数を扱えますが、内部的には2進数で表現されるため、10進数の小数を正確に表現できない場合があります。
そのため、金額、税金、請求金額、精算金額のように正確性が重要な場面では BigDecimal を使うのが基本です。
- BigDecimal は正確な10進数計算に向いている
- 金額や税金の計算では BigDecimal を使う
- BigDecimal は文字列から生成するのが安全
- 足し算は add、引き算は subtract、掛け算は multiply、割り算は divide を使う
- 割り算では RoundingMode を指定する
- 比較では equals ではなく compareTo を使う
- 平均値やグラフ表示など、近似値でよい場合は double でもよい
BigDecimal は、業務システムではよく使う重要なクラスです。
特に金額計算を扱う場合は、double との違い、生成方法、丸め処理、比較方法をしっかり理解しておくとよいです。


