このコード:
System.out.println(Math.abs(Integer.MIN_VALUE));
-2147483648
を返します
2147483648
として絶対値を返すべきではありませんか?
Integer.MIN_VALUE
は-2147483648
ですが、32ビット整数に含めることができる最大値は+2147483647
です。 32ビット整数で+2147483648
を表現しようとすると、-2147483648
に効果的に「ロールオーバー」します。これは、符号付き整数を使用する場合、+2147483648
と-2147483648
の2の補数バイナリ表現が同一であるためです。ただし、+2147483648
は範囲外と見なされるため、これは問題ではありません。
この問題についてもう少し読むには、 ウィキペディアのTwoの補数に関する記事 を参照してください。
あなたが指摘する行動は、実際、直感に反します。ただし、この動作は Math.abs(int)
のjavadoc で指定された動作です。
引数が負でない場合、引数が返されます。引数が負の場合、引数の否定が返されます。
つまり、Math.abs(int)
は次のJavaコードのように動作するはずです。
_public static int abs(int x){
if (x >= 0) {
return x;
}
return -x;
}
_
つまり、負の場合、_-x
_です。
JLSセクション15.15.4 によると、_-x
_は_(~x)+1
_と等しく、ここで_~
_はビットごとの補数演算子です。
これが正しいかどうかを確認するには、-1を例としてみましょう。
整数値_-1
_は、Java(これをprintln
または他の方法で確認してください)の16進数の_0xFFFFFFFF
_として記録できます。 -(-1)
は次のようになります。
_-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
_
だから、それは動作します。
_Integer.MIN_VALUE
_ で試してみましょう。最小の整数は_0x80000000
_で表現できること、つまり、最初のビットを1に設定し、残りの31ビットを0に設定できることを知っていると、次のようになります。
_-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1
= 0x80000000 = Integer.MIN_VALUE
_
そして、これがMath.abs(Integer.MIN_VALUE)
が_Integer.MIN_VALUE
_を返す理由です。また、_0x7FFFFFFF
_は_Integer.MAX_VALUE
_であることに注意してください。
そうは言っても、この直感に反する戻り値による問題を将来どのように回避できますか?
@ Bombeが指摘したように で、以前にint
sをlong
にキャストできました。しかし、私たちは
int
sにキャストし直します。Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
のため機能しません。Math.abs(long)
もあるので、_Long.MIN_VALUE
_に等しい値でMath.abs(Long.MIN_VALUE) == Long.MIN_VALUE
を決して呼び出さないことを期待して、long
sに進みます。BigInteger.abs()
は常に正の値を返すため、どこでもBigInteger
sを使用できます。これは、生の整数型を操作するよりも少し遅い、優れた代替手段です。
次のように、Math.abs(int)
の独自のラッパーを作成できます。
_/**
* Fail-fast wrapper for {@link Math#abs(int)}
* @param x
* @return the absolute value of x
* @throws ArithmeticException when a negative value would have been returned by {@link Math#abs(int)}
*/
public static int abs(int x) throws ArithmeticException {
if (x == Integer.MIN_VALUE) {
// fail instead of returning Integer.MAX_VALUE
// to prevent the occurrence of incorrect results in later computations
throw new ArithmeticException("Math.abs(Integer.MIN_VALUE)");
}
return Math.abs(x);
}
_
int positive = value & Integer.MAX_VALUE
_(本質的に_Integer.MAX_VALUE
_から_0
_ではなく_Integer.MIN_VALUE
_にオーバーフローします)最後に、この問題はしばらくの間知られているようです。たとえば、 対応するfindbugsルールに関するこのエントリ を参照してください。
Java docが javadoc のMath.abs()に対して言っていることは:
引数がInteger.MIN_VALUE(最も負の表現可能なint値)の値と等しい場合、結果は負の同じ値になることに注意してください。
期待する結果を確認するには、Integer.MIN_VALUE
からlong
へ:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
2147483648はJavaの整数に格納できません。そのバイナリ表現は-2147483648と同じです。
だが (int) 2147483648L == -2147483648
正の等価物がない負の数が1つあるため、正の値はありません。 Long.MAX_VALUEでも同じ動作が見られます。