【Java】BigDecimalでの0除算を徹底解説|初心者向けサンプル付き

お疲れ様です。はるさらと申します。

Javaで数値計算を扱う際に便利なクラスとしてBigDecimalがあります。
精度の高い計算が可能な一方で、「0除算」に関しては注意が必要です。
この記事では、経験の浅い方にも分かりやすいように、
BigDecimalでの0除算の挙動や対策方法を具体例を交えて解説します。


BigDecimalでの0除算はどうなるのか?

結論から言うと、BigDecimalで0除算を行うと ArithmeticException が発生します。
intやdoubleと異なり、BigDecimalは例外を投げる仕様です。

これは安全に数値計算を行うための仕組みですが、
経験の浅い方にとっては意外な落とし穴になることがあります。


Javaの他の型との違いを確認する

まずは、BigDecimal以外の型で0除算を行った場合の挙動を比較してみましょう。

public class DivisionSample {
    public static void main(String[] args) {
        // int型の0除算
        try {
            int result1 = 10 / 0; // ArithmeticException
        } catch (Exception e) {
            System.out.println("int: " + e);
        }

        // double型の0除算
        double result2 = 10.0 / 0.0; // Infinity
        System.out.println("double: " + result2);

        // BigDecimal型の0除算
        try {
            BigDecimal a = new BigDecimal("10");
            BigDecimal b = BigDecimal.ZERO;
            BigDecimal result3 = a.divide(b); // ArithmeticException
        } catch (Exception e) {
            System.out.println("BigDecimal: " + e);
        }
    }
}

実行結果は以下の通りです。

int: java.lang.ArithmeticException: / by zero
double: Infinity
BigDecimal: java.lang.ArithmeticException: Division by zero

doubleの場合はInfinityになりますが、
BigDecimalでは例外が発生します。
この点を理解しておかないと、思わぬバグにつながります。


BigDecimal.divideでエラーになるケースと対策

基本的な例外処理の書き方

BigDecimalを使う際には、0除算を明示的にチェックするのが基本です。

BigDecimal numerator = new BigDecimal("100");
BigDecimal denominator = BigDecimal.ZERO;

if (denominator.compareTo(BigDecimal.ZERO) == 0) {
    System.out.println("0除算はできません");
} else {
    BigDecimal result = numerator.divide(denominator);
    System.out.println(result);
}

compareTo(BigDecimal.ZERO)を使うと、
分母が0かどうかを判定できます。


実務でよく使う「割り算+丸め」のサンプル

実務では、0ではない分母でも「割り切れないケース」が多々あります。
そのため、divideには丸めモードを指定することが一般的です。

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

丸めモードを指定することで、ArithmeticException: Non-terminating decimal expansion を回避できます。


代替手法の比較

① 例外処理で対応する方法

        BigDecimal numerator = new BigDecimal("10");
        BigDecimal denominator = BigDecimal.ZERO;

        try {
            BigDecimal result = numerator.divide(denominator);
            System.out.println("結果: " + result);
        } catch (ArithmeticException e) {
            System.out.println("分母が0なので処理できません");
        }

→ 例外処理を利用するパターン。
シンプルですが例外を多用すると可読性が落ちます。


② 事前チェックで対応する方法

if (denominator.compareTo(BigDecimal.ZERO) != 0) {
    BigDecimal result = numerator.divide(denominator, 2, RoundingMode.HALF_UP);
}

→ 実務ではこちらの方が好まれます。無駄に例外を発生させないため、
パフォーマンスやログのノイズが減ります。


③ Optionalを使ったラップ

Java8以降ならOptionalを使って、0除算を安全にラップできます。

public static Optional<BigDecimal> safeDivide(BigDecimal num, BigDecimal den) {
    if (den.compareTo(BigDecimal.ZERO) == 0) {
        return Optional.empty();
    }
    return Optional.of(num.divide(den, 2, RoundingMode.HALF_UP));
}

// 利用側
safeDivide(new BigDecimal("10"), BigDecimal.ZERO)
    .ifPresentOrElse(
        v -> System.out.println("結果: " + v),
        () -> System.out.println("分母が0でした")
    );

→ 事前チェックを関数化することで、コードの再利用性が高まります。


注意点:経験の浅い方が間違えやすいポイント

  1. equalsではなくcompareToを使う
    BigDecimalはスケールの違いも区別するため、new BigDecimal("0.0").equals(BigDecimal.ZERO)false になります。
    compareTo(BigDecimal.ZERO) == 0 を使うこと。
  2. 丸めモードを指定しないと例外が発生する
    1 / 3 のような割り算では、丸め指定がないと ArithmeticException になります。
  3. doubleとの違いを理解する
    doubleならInfinityになりますが、BigDecimalでは必ず例外です。

関連記事の紹介

今回のテーマと関連のある記事も参考にしてみてください。


まとめ

  • BigDecimalで0除算を行うと必ずArithmeticExceptionが発生する
  • 事前にcompareTo(BigDecimal.ZERO)でチェックするのが安全
  • 割り算の際には丸めモードを指定することが必須
  • 実務では事前チェック+丸めモードの組み合わせが基本

BigDecimalは金融システムなどで必須のクラスですが、
挙動を正しく理解していないと大きなバグの原因になります。
今回のサンプルを参考に、安全に利用してみてください。

どなたかのお役に立てば幸いです。
それではまたー!