Java | Java 標準ライブラリ:BigDecimal の丸めモード

Java Java
スポンサーリンク

なぜ BigDecimal に「丸めモード」が必要なのか(全体像)

BigDecimal は「小数を誤差なく扱う」ためのクラスですが、
もう一つ大事なテーマがあります。

「小数をどこかで“切り捨て・切り上げ・四捨五入”しないといけないとき、そのルールをどう決めるか」

これが「丸めモード(RoundingMode)」です。

例えば、
1.005 を小数第2位までにしたいとき、
1.00 にするのか、1.01 にするのか、場合によってルールが違います。

お金・税金・利息・料金など、現実世界のルールが絡む計算では、
この「丸め方」を間違えると、ビジネスロジックとしてアウトになります。

BigDecimal の丸めモードは、

どの桁で切るか(scale: 小数桁数)
どう切るか(RoundingMode: 丸め方)

を、コードの中でハッキリ指定するための仕組みです。


基本の使い方:setScale と RoundingMode の組み合わせ

setScale で「小数点以下何桁にするか」を指定する

BigDecimal の桁数を揃えたいときに使うのが setScale です。

BigDecimal x = new BigDecimal("1.2345");

BigDecimal y = x.setScale(2, RoundingMode.HALF_UP);  // 小数第2位まで
System.out.println(y);  // 1.23
Java

ここで

第1引数:2 → 小数点以下 2 桁にする
第2引数:RoundingMode.HALF_UP → 一般的な四捨五入

を意味します。

「丸めモード」の話は、この RoundingMode.◯◯ の部分が主役です。


まずはこれだけ覚えればいい 3 モード(超重要)

丸めモードはたくさんありますが、
初心者のうちは、まず「この3つ」をしっかり押さえるのがおすすめです。

HALF_UP:いわゆる普通の四捨五入

一番なじみのあるルールです。

  • 0〜4 → 切り捨て
  • 5〜9 → 切り上げ

の、学校で習った「四捨五入」です。

BigDecimal x1 = new BigDecimal("1.234");
BigDecimal x2 = new BigDecimal("1.235");
BigDecimal x3 = new BigDecimal("1.236");

System.out.println(x1.setScale(2, RoundingMode.HALF_UP));  // 1.23
System.out.println(x2.setScale(2, RoundingMode.HALF_UP));  // 1.24
System.out.println(x3.setScale(2, RoundingMode.HALF_UP));  // 1.24
Java

多くの「通常の金額計算」は、この HALF_UP で進めることが多いです。
(ただし、業界によって別ルールを求められることもあります)

DOWN:単純な切り捨て(0 方向)

RoundingMode.DOWN は「小数点以下を全部捨てる」イメージです。
0 の方向に寄せる、と思ってください。

BigDecimal x1 = new BigDecimal("1.239");
BigDecimal x2 = new BigDecimal("-1.239");

System.out.println(x1.setScale(2, RoundingMode.DOWN));  // 1.23
System.out.println(x2.setScale(2, RoundingMode.DOWN));  // -1.23
Java

ポイントは、マイナスでも「0 に近づく」形になることです。

1.239 → 1.23
-1.239 → -1.23

となります。

「常にお客さんに不利にならないよう、1円未満は切り捨て」
のようなルールなら、この DOWN を選ぶことがあります。

UP:単純な切り上げ(0 から離れる方向)

RoundingMode.UP は DOWN の逆で、「0 から離れる方向」に切り上げます。

BigDecimal x1 = new BigDecimal("1.231");
BigDecimal x2 = new BigDecimal("-1.231");

System.out.println(x1.setScale(2, RoundingMode.UP));  // 1.24
System.out.println(x2.setScale(2, RoundingMode.UP));  // -1.24
Java

1.231 → 1.24
-1.231 → -1.24

マイナス値も「絶対値が大きくなる方向」に動きます。

「必ず会社側に有利な方向に丸める」
「容量制限を超えないよう、少し余裕を見て上に丸める」

といった特殊なルールで使われます。


HALF_EVEN(銀行丸め)を直感で理解する

なぜ「5 をいつも切り上げる」のがダメな場合があるのか

HALF_UP(普通の四捨五入)は、「.5 は必ず上に行く」ルールです。
大量のデータを丸め続けると、少しずつ「上方向に偏った誤差」が積み上がります。

金融・統計・大規模集計の世界では、この「偏り」が問題になることがあります。

そこで登場するのが RoundingMode.HALF_EVEN、いわゆる「銀行丸め」です。

HALF_EVEN のルール

ざっくり言うと、

「ちょうど真ん中 (.5) のときだけ、“結果の下1桁が偶数になる方” に丸める」

というルールです。

例として、小数第1位で丸めてみます。(setScale(0, HALF_EVEN) と同じイメージ)

1.5 → 2(2 は偶数)
2.5 → 2(2 は偶数)
3.5 → 4(4 は偶数)
4.5 → 4(4 は偶数)

Java のコードで、もう少し現実的な例を見ます。

BigDecimal a = new BigDecimal("1.25");
BigDecimal b = new BigDecimal("1.35");

System.out.println(a.setScale(1, RoundingMode.HALF_UP));    // 1.3
System.out.println(b.setScale(1, RoundingMode.HALF_UP));    // 1.4

System.out.println(a.setScale(1, RoundingMode.HALF_EVEN));  // 1.2
System.out.println(b.setScale(1, RoundingMode.HALF_EVEN));  // 1.4
Java

HALF_UP では、1.25 も 1.35 も「5 なので上へ」で、
それぞれ 1.3, 1.4 になります。

HALF_EVEN では、

1.25 → 1.2(下1桁が偶数になる 1.2 へ)
1.35 → 1.4(下1桁が偶数になる 1.4 へ)

というように、.5 のときだけ「偶数側」に寄せます。

大量に丸めると、「上に行く回数」と「下に行く回数」がバランスしやすくなり、
「偏りの少ない丸め」が得られることが期待されます。

銀行や統計処理など、「長期的な公平さ」が求められる世界でよく使われます。


丸めモードがないとどうなるか(ArithmeticException)

divide で丸めモードを指定しないと怒られるケース

BigDecimal の割り算でよくあるのが、「割り切れないのに丸めモードを指定しない」問題です。

BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("3");

BigDecimal c = a.divide(b);  // ここで ArithmeticException: Non-terminating decimal expansion
Java

1 ÷ 3 は 0.3333… と無限に続く小数です。
「どこまで続けるの?」を指定しないまま divide すると、Java は怒ります。

この場合は、scaleRoundingMode をセットで指定します。

BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("3");

BigDecimal c = a.divide(b, 2, RoundingMode.HALF_UP);  // 小数第2位まで四捨五入
System.out.println(c);  // 0.33
Java

あるいは、「scale は後から setScale で揃える」という考え方もありますが、
割り算は最初から「どこで丸めるか」を決めておいたほうが安全です。


どの丸めモードを選ぶかを判断する軸

ビジネスルールに合わせるのが最優先

正解は「一般論」ではなく、「そのシステムのルール」です。

例えば:

・一般的な金額表示
→ 小数第2位(1円)まで HALF_UP(普通の四捨五入)

・端数は必ずお客さんに有利にしたい(1円未満は切り捨て)
→ DOWN

・容量や枠を超えないように、常に安全側に見積もる
→ UP

・長期的な統計・利息・精算などで偏りを減らしたい
→ HALF_EVEN

という感じで、「現実世界のルール」をまず確認してから、
それに対応する RoundingMode を選びます。

コード内で「何をしたいか」が伝わることが大事

例えば、「税込価格を計算して、小数第2位まで四捨五入する」なら:

BigDecimal taxRate = new BigDecimal("0.1");
BigDecimal price = new BigDecimal("123");

BigDecimal tax = price.multiply(taxRate);                      // 12.3
BigDecimal total = price.add(tax).setScale(2, RoundingMode.HALF_UP);  // 135.30
Java

ここで、setScale(2, RoundingMode.HALF_UP) と書いてあると、
「1円単位で四捨五入しているんだな」と読み手にも伝わります。

丸めモードをコードの中で明示することは、
「バグを防ぐ」というより、「仕様をコードに刻む」意味合いが大きいです。


まとめ:BigDecimal の丸めモードをどう捉えておくか

初心者として、まずはこのように整理できていれば十分です。

  • BigDecimal は「どの桁で」「どう丸めるか」を自分で決めてあげないといけない
  • setScale(桁数, RoundingMode.◯◯) で、「桁」と「丸め方」をセットで指定する
  • よく使うのは HALF_UP(普通の四捨五入)、DOWN(切り捨て)、UP(切り上げ)、必要に応じて HALF_EVEN(銀行丸め)
  • divide で割り切れないときは、必ず丸めモードを指定する

タイトルとURLをコピーしました