Java | 基礎文法:ArithmeticException

Java Java
スポンサーリンク

ArithmeticException の全体像

ArithmeticException は「算術計算が不正で続行できない」ときに投げられる実行時例外です。代表例は整数の 0 除算(割り算・剰余の分母が 0)。浮動小数点(double/float)の 0 除算は例外にならず、Infinity や NaN が返る点が重要な違いです。BigDecimal などの高精度計算でも、条件次第で ArithmeticException が発生します。


どんなときに発生するか

整数の 0 除算(割り算・剰余)

int a = 10;
int b = 0;
// int c = a / b;  // ArithmeticException: / by zero
// int r = a % b;  // ArithmeticException: / by zero
Java

int/long の割り算・剰余は分母が 0 だと例外。try-catch で捕まえられますが、通常は事前に 0 チェックで防ぎます。

浮動小数点の 0 除算は例外にならない

double x = 1.0;
double y = 0.0;
System.out.println(x / y);  // Infinity(例外なし)
System.out.println(y / y);  // NaN(例外なし)
Java

double/float は IEEE 754 に従い、例外ではなく特殊値を返します。数値が Infinity/NaN になって以降の計算も壊れやすく、明示的な検査が必要です。

BigDecimal の「丸め無し割り算」

import java.math.*;
BigDecimal p = new BigDecimal("1");
BigDecimal q = new BigDecimal("3");
// BigDecimal r = p.divide(q); // ArithmeticException: 非終端小数のため丸めが必要
BigDecimal r2 = p.divide(q, 10, RoundingMode.HALF_UP); // 10桁に丸めて安全
Java

割り算結果が有限小数にならないとき、丸め指定なしの divide は例外になります。必ず scale(桁数)と RoundingMode を指定しましょう。

BigInteger の 0 除算

import java.math.BigInteger;
BigInteger a = BigInteger.TEN;
BigInteger z = BigInteger.ZERO;
// a.divide(z); // ArithmeticException: Division by zero
Java

任意精度整数でも分母 0 は例外です。

オーバーフロー検知系のメソッド

int a = Integer.MAX_VALUE;
int b = 1;
// a + b;                  // オーバーフローするが「例外は出ない」
Math.addExact(a, b);       // ArithmeticException: integer overflow
Java

通常の int/long 演算はオーバーフローしても例外になりません(値が循環)。確実に検知したいときは Math.addExact/subtractExact/multiplyExact を使います。


重要ポイントの深掘り:例外にならない「危険な状態」

「オーバーフローは黙って起きる」

int/long の加算・乗算は上限を超えると勝手に値が回り込み、誤結果になります。安全性が必要な場面では Exact 系メソッドか、BigInteger/BigDecimal を使う発想が有効です。

Infinity/NaN を放置しない

double/float では 0 除算や未定義演算で Infinity/NaN が返ります。以降の計算は伝播して結果が無意味になるため、節目でチェックします。

double v = 1.0 / 0.0; // Infinity
if (Double.isInfinite(v) || Double.isNaN(v)) {
    // ここで補正・ログ・フォールバック
}
Java

予防と安全な書き方

0 チェックで前倒し検証

static int safeDivide(int a, int b) {
    if (b == 0) throw new IllegalArgumentException("divisor must not be 0");
    return a / b;
}
Java

例外を try-catch に頼るより、契約違反を入口で弾いて明確化するほうが読みやすく安全です。

BigDecimal は必ず丸め指定

import java.math.BigDecimal;
import java.math.RoundingMode;

static BigDecimal ratio(BigDecimal num, BigDecimal den) {
    if (den.compareTo(BigDecimal.ZERO) == 0)
        throw new IllegalArgumentException("denominator must not be 0");
    return num.divide(den, 8, RoundingMode.HALF_UP); // 8桁で丸め
}
Java

丸めと桁数の方針を定数化して全体で統一すると、ぶれなくなります。

オーバーフローを検知する

static int safeAdd(int a, int b) {
    return Math.addExact(a, b); // 溢れたら ArithmeticException
}
Java

金融やカウンタの上限管理など、誤結果が許されない場面で有効です。


例題で身につける

例 1: 整数割り算の安全ラッパー

public class IntDiv {
    public static int div(int a, int b) {
        if (b == 0) throw new IllegalArgumentException("divisor must not be 0");
        return a / b;
    }
    public static void main(String[] args) {
        System.out.println(div(10, 2));  // 5
        System.out.println(div(10, 0));  // IllegalArgumentException
    }
}
Java

例 2: BigDecimal 割り算の丸め方針を固定

import java.math.*;

public class Ratios {
    private static final int SCALE = 6;
    private static final RoundingMode MODE = RoundingMode.HALF_UP;

    static BigDecimal percent(BigDecimal part, BigDecimal total) {
        if (total.compareTo(BigDecimal.ZERO) == 0) return BigDecimal.ZERO;
        return part.multiply(BigDecimal.valueOf(100))
                   .divide(total, SCALE, MODE);
    }

    public static void main(String[] args) {
        System.out.println(percent(new BigDecimal("1"), new BigDecimal("3"))); // 33.333333
    }
}
Java

例 3: 浮動小数点の特殊値チェック

public class FPGuard {
    static double safeDiv(double a, double b) {
        double r = a / b; // 0除算でも例外にならない
        if (Double.isInfinite(r) || Double.isNaN(r)) throw new ArithmeticException("invalid FP result");
        return r;
    }
    public static void main(String[] args) {
        System.out.println(safeDiv(1.0, 2.0)); // 0.5
        System.out.println(safeDiv(1.0, 0.0)); // ArithmeticException
    }
}
Java

例 4: オーバーフローを検知するカウンタ

public class Counter {
    private int value = 0;
    public void add(int delta) {
        value = Math.addExact(value, delta); // 溢れたら即例外
    }
    public int get() { return value; }
}
Java

仕上げのアドバイス(重要部分のまとめ)

ArithmeticException の主因は「整数の 0 除算」と「高精度演算の丸め未指定」です。0 チェックを前倒しにして契約違反を弾く、BigDecimal は必ず桁数と丸めを指定、浮動小数点は例外にならないので Infinity/NaN を検知、整数オーバーフローは黙って起きるため Exact 系メソッドや BigInteger を使う——これで算術由来の不具合は大幅に減ります。

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