今日はネタをひとつ。以下のプログラムを実行すると、
標準出力には何が出力されるだろうか。

public class Test {
  static public void main(String[] args) {
    System.out.println((long)(80000 * 90000));
  }
}

これを即答できた人は、今日の日記に用はない。

最近、Java を使う機会が多いのだが、
一番気にしているのが、数値型の範囲や型変換に関する仕組みだ。

Java の基本型 byte が符号付きなのは初歩として、
byte <= short <= int <= long 間のキャストや、
四則演算において、型のオーバーフローがチェックされない事である。

C 言語を知っている者には、値の切り詰めには抵抗がないだろうが、
強力な例外機構を持っている Java としては、
非常に不思議な仕様と感じるのは私だけだろうか。

この例は、80,000 と 90,000 の掛け算だ。
Java の数値リテラルは、既定で int 型である。
そのため、これは int と int の乗算となる。

理論的には 32 ビット数値の積は、64 ビットの結果を生むのだが、
Java においては、int と int の乗算の結果も int である。

80,000 × 90,000 は、算数では 7,200,000,000 だが、
int 型の正の上限は Integer#MAX_VALUE として定数化されている
2,147,483,647 であるため、int には収まらない。

この際 Java では例外を生成せず、ビット単位の切り落としを行う。
int は 32 ビットであるため、結果のうち下位の 32 ビットのみが残る。

7,200,000,000 は 16 進表現で 1ad274800 であるため、
残る 32 ビットは、ad274800 である。
これが乗算の結果の値として使用される。

Java では、整数は 2 の補数表現を使って格納されるため、
最上位ビットが 1 である 0xad274800 は負の値であり、
十進表現に直すと、-1389934592 となる。

上記式では計算結果を long にキャストしているが、
時すでに遅し、乗算の時点で int 型 -1389934592 なので、
long にキャストしたとしても、その値に変化はない。
単に、PrintStream#println の long 用メソッドが呼ばれるだけだ。

つまり、問題の答えは「-1389934592」である。

これが何を意味するかというと、値の範囲によっては、
計算結果が「自然でない値」になる可能性があるということだ。
これは乗算だけでなく、四則演算や縮小キャスト等広く当てはまる。

上記はあくまでも例なので極端だが、
プログラム中で、変数や引数を使って計算することは多いだろう。
さて、あなたは、こういった値のチェックをしているかな?