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 について、基本的な考え方から実務でよく使う書き方までまとめます。

スポンサーリンク

※このページにはプロモーションが含まれています。当サイトは各種アフィリエイトプログラムから一定の収益を得ています。

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 との違い、生成方法、丸め処理、比較方法をしっかり理解しておくとよいです。

スポンサーリンク

Java