Javaのサイレントキラー - 整数オーバーフロー、注意深い!

Javaのサイレントキラー-整数オーバーフロー、注意!

信じられないかもしれませんが、Javaには整数バッファオーバーフローも含まれています。 これがそれを説明する正しい単語かどうかはわかりませんが、いくつか提案できますか:)

OK、以下のプログラムを見てください。このプログラムは1日のマイクロ秒数を出力します。

public class JavaLongOverflow {

    public static void main(String[] args) {

        final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;

        System.out.println("MICROS_PER_DAY : " + MICROS_PER_DAY);

    }

}

結果

MICROS_PER_DAY : 500654080

Oh.. 答えは500654080です。 待つ.. 答えは正しいですか? 正解は86400000000である必要があります!!! Javaが不正確な結果を与えるのはなぜですか? この番号はどこから来たのですか? 正確に何が起こったのですか?

これは「int」オーバーフローの原因だと思います。 はい、「長いMICROS_PER_DAY」変数には86400000000のような多数に収まる十分なスペースがありますが、「int」には収まりません! 「int」の最大数は2147483647です。 上記のプログラムに注意してください。算術全体は「int」であり、結果はlongに昇格され、「long MICROS_PER_DAY」変数に割り当てられます。 According to [JLS 5.1.2], the promotion from int to long is cited as widening primitive conversion which preserves the (incorrect) numerical value.

ただし、最初の整数の「long」を明示的に指定することにより、上記のサイレントキラーオーバーフローを回避して、演算全体が長い演算になるようにすることができます。

public class JavaLongOverflow {

    public static void main(String[] args) {

        final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;

        System.out.println("MICROS_PER_DAY : " + MICROS_PER_DAY);

    }

}

結果

MICROS_PER_DAY : 86400000000

上記のプログラムは、86400000000を期待どおりに出力します

結論

このバグは、検出または認識が非常に困難です。 時にはそれを検出することさえ不可能です。 誰かが金融システムでこの種の間違いを犯した場合、その結果を想像することはできません。 私の唯一のアドバイスは、2つの異なるタイプのプリミティブタイプの計算を処理するときに、プリミティブタイプを明示的にキャストすることです。 すべての計算でプリミティブ変換を自動的に拡張することは常に避けてください。