【Java学習|豆知識】Javaの「剰余演算子(%)」で陥る罠:負の数の扱いを正しく理解する

導入:なぜ剰余演算の理解が重要なのか

Java開発において、偶数・奇数の判定や、配列のローテーション、ハッシュ値の計算などで「剰余演算子(%)」は頻繁に登場します。しかし、多くのエンジニアが「%は余りを求めるもの」と単純に考えており、負の数が絡んだ際の挙動で思わぬバグを生むことがあります。この挙動を正確に理解しておくことは、堅牢なシステムを構築する上で非常に重要です。

基礎知識:剰余演算子の仕組み

Javaの剰余演算子(%)は、数学的な「剰余(mod)」と、プログラミング言語における「剰余(remainder)」が異なる点に注意が必要です。
Javaの計算式「a % n」の結果は、「a = (a / n) n + (a % n)」という関係式を満たすように定義されています。ここで重要なのは、商(a / n)が「ゼロに近い整数へ切り捨てられる(0方向への切り捨て)」という点です。この仕様により、被除数が負の場合、結果も負になることがあります。

実装・解決策:負の数を考慮した正しい剰余

負の値を扱う計算で「常に正の余り(0以上n未満)」を得たい場合、単純に「%」を使うだけでは不十分です。例えば、循環リストのインデックス計算などで負のオフセットが発生すると、負のインデックスでArrayIndexOutOfBoundsExceptionが発生します。
このような場合は、「((a % n) + n) % n」という式を使うのが定石です。一度余りを求めた後に除数(n)を足し、再度nで割ることで、数学的な意味での剰余(0〜n-1)に補正できます。

サンプルプログラム

以下のコードを実行して、通常の「%」と、補正した計算の挙動を確認してみてください。

public class RemainderExample {
    public static void main(String[] args) {
        int dividend = -5;
        int divisor = 3;

        // 通常の剰余演算:結果は -2 になる
        int normalRemainder = dividend % divisor;
        System.out.println("通常の剰余: " + normalRemainder);

        // 正の数に補正する計算:結果は 1 になる
        // (-5 % 3) は -2。それに 3 を足すと 1。1 % 3 は 1。
        int positiveRemainder = ((dividend % divisor) + divisor) % divisor;
        System.out.println("補正後の剰余: " + positiveRemainder);
    }
}

応用・注意点:現場での活用と落とし穴

1. 偶数・奇数判定の注意点
「if (i % 2 == 1)」という判定は、iが負の奇数の場合に「-1」となるため、期待通りに動作しません。奇数判定を行う際は「if (i % 2 != 0)」と書くか、「if (Math.abs(i) % 2 == 1)」とするのが安全です。

2. パフォーマンスへの配慮
ビット演算(&)を使えるのは「除数が2のべき乗(2, 4, 8…)」の場合のみです。例えば「i % 8」は「i & 7」と書くことで高速化できますが、負の数に対しては挙動が全く異なるため、コードの可読性と正確性を優先し、基本は%演算子を使用することを推奨します。

3. ゼロ除算
当然ですが、0で割ろうとすると ArithmeticException が発生します。計算の入力値が動的に変わる場合は、必ず除数が0でないことをチェックするガード句を忘れないようにしましょう。

コメント

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