Java には Math.abs( i ) というメソッドがあります。
これはアンチロックブレーキシステムでもなければ、アンバサ(Ambasa)の略でもなく、「Absolute Value」 = 絶対値を取る機能です。
例えばMath.abs( -500 ) と書くと、「500」が返ってきます。
/** 実行例 */
int i = Math.abs( -500 );
System.out.println( i );
// => 500
ところで、このMath.absに、Integer.MIN_VALUE(Integerの中で最も小さな数 = -2147483648)を渡すとどうなるでしょう。
Javaのintは32ビットです。32ビットの数値が指す値は-2147483648~2147483647です。-2147483648 を abs すると、そのままマイナスが取れて 2147483648 が返ってきそうですが、この値はintの精度の中に納まっていません。
そのため、-2147483648を渡すとこうなります。
/** 実行例 */
int i = Math.abs( -2147483648 );
System.out.println( i );
// => -2147483648
そのまま-2147483648が返ってきてますね。
どう見てもこれは絶対値じゃありません。
でも、戻り値がintである以上、2147483648は返しようがないので、正しい値が返らないのは仕方がないところではあります。これの為にわざわざunsigned intにしたり(Javaにはunsignedはないけど)、longにしたらちょっと馬鹿馬鹿しいですしね。
ちなみにこの動作はJavaの仕様的にこうなっているようです。
引数が Integer.MIN_VALUE の値 (int の最小値) と等しい場合は、結果も同じ値 (負の値) になります。
http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/Math.html#abs%28int%29
試しに我が家のVisualC++で同じ動作をさせてみたところ、同じ結果になりました。Cの場合はこの動作については未定義で、各コンパイラに任されているそうです。
int _tmain(int argc, _TCHAR* argv[])
{
int i = abs(-2147483648);
std::cout << i;
// => -2147483648
return 0;
}
恒例の中の人見学の時間です。absの処理は、内部的にはどんな風に行われているのでしょうか?
これがabsの中の人です。
/** absの中身 */
public static int abs(int a) {
return (a < 0) ? -a : a;
}
変態的ソースが出てくると期待していた方はすいません。ごく普通のソースですね。
三項演算子で、0未満ならマイナスして返し、0以上ならそのまま返す。とても一般的なソースですね。
ところで、1つ不思議なこと(知っている人は不思議に思わないかもしれないけど)があります。
先ほどのソースを見る限り、absに-2147483648が引き渡された時は、-1が掛けられています。ということは、-2147483648 は -1 を掛けても -2147483648なんでしょうか?
実際に試してみましょう。
/** Integer.MIN_VALUE に -1 を掛けてみる */
int i = -2147483648 * -1;
System.out.println( i );
// => -2147483648
予定通りの結果になりました。マイナス1を掛けても同じ数字のままです。
Javaの32ビットによる数値表現において、-1を掛けるということは、2の補数を取るということと同義です。
では、-2147483648の2進数表記はどんな値になるでしょう?
/** Integer.MIN_VALUE の2進数表記 */
int i = -2147483648;
System.out.println( Integer.toBinaryString( i ) );
// => 10000000000000000000000000000000
10000000000000000000000000000000になるそうです。
32ビットの先頭だけが「1」で、残りは全部「0」という表記です。
これの2の補数はどういった値になるでしょうか?
2の補数は、全ビットを反転して1を足せば取れます。まず反転すると下記のようになります。
01111111111111111111111111111111
これに1を足すと、右から1ビット目~31ビット目が全部1なので、32ビットまで繰り上がって、結果はこうなります。
10000000000000000000000000000000
見事に元の値ですね。
というわけで、-2147483648を使うときは気をつけましょう、というお話でした。
Integer.MIN_VALUEだろうが、MAX_VALUEだろうが、その辺の値は1足したり引いたりするだけで溢れる、オーバーフロー一歩手前の子なので、こんなレアケースよりもっと危険なことが溢れている気もしますが。