
はじめに
プログラム上で小数をきちんと扱えていますか…?
Javaで小数を扱う際に、float や double をそのまま使うと、
0.1 + 0.2 = 0.30000000000000004
のような誤差に悩まされることがあります。この誤差を避けるために登場するのが BigDecimal クラスです。
しかし、BigDecimal は使えば良いものでなく、扱い方を誤ると逆にバグの温床にもなります。
この記事では、BigDecimalの基本から丸め、比較、equalsの注意点までを体系的に解説します
BigDecimalとは?
BigDecimal は 高精度な10進数演算 をサポートするクラスです。主に以下のような用途で使われます。
なぜBigDecimal を使うのか?
まず前提として、float や double は誤差を含む型です。コンピュータ内部では 2進数(0と1) で数値を表現しています。ところが、10進数の0.1や0.2は2進数で「有限桁」で表現できません。たとえば、0.1 を2進数に変換すると次のように「無限に続く小数」になります。
0.1(10進数) = 0.0001100110011001100110011...(2進数)
このように 循環小数(無限小数) になるため、コンピュータは途中で打ち切って「近似値」として保持します。このわずかな誤差が、計算を繰り返すうちに積み重なり、結果として
0.1 + 0.2 = 0.30000000000000004
のようなズレが発生します。
BigDecimalの正しい生成方法
import java.math.BigDecimal;
BigDecimal a = new BigDecimal("0.1"); // ✅ 安全
BigDecimal b = BigDecimal.valueOf(0.1); // ✅ 安全(内部で文字列化)
BigDecimal c = new BigDecimal(0.1); // ❌ 危険:誤差を含む
new BigDecimal(double) は危険!
System.out.println(new BigDecimal(0.1)); // 0.1000000000000000055511151231...
内部の2進表現がそのまま反映されるため、必ず文字列か valueOf() を使うのがベストです。

誤差を含んだdouble型で生成するから、結局誤差があるものが生み出されるんだね…。
基本的な演算
BigDecimal price = new BigDecimal("1200");
BigDecimal taxRate = new BigDecimal("0.1");
BigDecimal total = price.multiply(BigDecimal.ONE.add(taxRate));
System.out.println(total); // 1320.0
主な演算メソッド
| メソッド | 説明 |
|---|---|
add() | 加算 |
subtract() | 減算 |
multiply() | 乗算 |
divide() | 除算(※注意点あり) |
divide() には要注意
除算は、結果が有限小数でない場合に ArithmeticException を投げます。
BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("3");
System.out.println(a.divide(b)); // ⚠️ 例外:Non-terminating decimal expansion
これを防ぐには、丸めモード(RoundingMode) を指定します。
丸め(RoundingMode)の使い方
import java.math.RoundingMode;
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP);
System.out.println(result); // 3.33
主なRoundingMode一覧
| モード | 説明 | 例(1.2345 → 2桁) |
|---|---|---|
HALF_UP | 四捨五入 | 1.23 → 1.23 1.235 → 1.24 |
HALF_DOWN | 五捨六入 | 1.235 → 1.23 |
HALF_EVEN | 偶数丸め(銀行丸め法) もっとも近い数字に | 1.235 → 1.24 1.245 → 1.24 |
UP | 常に切り上げ (0から遠ざかる方向へ) | 1.231 → 1.24 -1.231 → -1.24 |
DOWN | 常に切り捨て (0に近づく方向へ) | 1.239 → 1.23 -1.239 → -1.23 |
CEILING | 正方向に丸め | 1.239 → 1.23 -1.239 → -1.23 |
FLOOR | 負方向に丸め | 1.239 → 1.23 -1.239 → -1.24 |

やりたいことをきちんと理解して、選択する必要がありそうだね!
比較は、equals() ではなく compareTo()
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("1.00");
System.out.println(a.equals(b)); // false
System.out.println(a.compareTo(b)); // 0(等しい)
なぜ違う?
equals()は スケール(小数点以下の桁数)まで比較します。
→ つまり、1.0と1.00は別物と判断されます。compareTo()は 数値的な値を比較します。
→ 同じ数値であればスケールが違っても 0 を返します。
大小比較
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("9.8");
System.out.println(a.compareTo(b) > 0); // true(a > b)
System.out.println(a.compareTo(b) == 0); // false(a == b)
System.out.println(a.compareTo(b) < 0); // false(a < b)

数値の大小比較は必ず compareTo() を使うのが鉄則です。
setScale() で桁数を揃える
BigDecimal price = new BigDecimal("123.4567");
BigDecimal rounded = price.setScale(2, RoundingMode.HALF_UP);
System.out.println(rounded); // 123.46
setScale() は小数点以下の桁数を統一するのに便利です。
金額処理では「常に2桁(円未満切り上げ)」などの指定がよく行われます。
まとめ
BigDecimal は一見難しく見えますが、「生成方法」「丸め」「比較」の3点さえ押さえれば怖くありません。金額や税計算など誤差が許されない場面では、「double は使わず、BigDecimal + RoundingMode + compareTo()」が鉄則です。
