このコード:
System.out.println(Math.abs(Integer.MIN_VALUE));
戻り値 -2147483648
絶対値をとして返すべきではありません2147483648
か?
このコード:
System.out.println(Math.abs(Integer.MIN_VALUE));
戻り値 -2147483648
絶対値をとして返すべきではありません2147483648
か?
回答:
Integer.MIN_VALUE
は-2147483648
ですが、32ビット整数に含めることができる最大値は+2147483647
です。+2147483648
32ビット整数で表現しようとすると、効果的にに「ロールオーバー」し-2147483648
ます。符号付き整数を使用する場合、の2の補数バイナリ表現からである+2147483648
とは-2147483648
同一です。ただし、+2147483648
範囲外と見なされるため、これは問題ではありません。
この問題についてもう少し詳しく知りたい場合は、2の補数に関するウィキペディアの記事を確認してください。
あなたが指摘する行動は、確かに直感に反しています。ただし、この動作はjavadocでMath.abs(int)
指定されている動作です。
引数が負でない場合は、引数が返されます。引数が負の場合、引数の否定が返されます。
つまり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
は0xFFFFFFFF
、Javaでは16進数のように記録できます(println
または他の方法でこれを確認してください)。-(-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)
です。long
呼び出さMath.abs(long)
れないことを期待して、sを続行します。Long.MIN_VALUE
Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
実際には常に正の値を返すBigInteger
ため、どこでもsを使用できBigInteger.abs()
ます。これは良い代替手段ですが、生の整数型を操作するよりも少し遅くなります。
次のように、の独自のラッパーを作成できます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ルールについてはこのエントリを参照してください。
期待する結果を確認するには、次のコマンドにキャストInteger.MIN_VALUE
しlong
ます。
System.out.println(Math.abs((long) Integer.MIN_VALUE));
Math.abs
負の数を返すことによって直感に反しているという事実を解決しませんMath.abs(Long.MIN_VALUE) == Long.MIN_VALUE
ArithmeticException
ますか?また、動作はAPIドキュメントに明確に文書化されています。
Math.abs(long)
。ここでの私の間違いをお詫びしますMath.abs(long)
。「アスカーが期待する結果を確認する」ための簡単な方法としてそれを示したときに、修正としての使用を提案したと思いました。ごめんなさい。
これに対する修正がJava15にあり、intおよびlongのメソッドになります。彼らはクラスに出席します
java.lang.Math and java.lang.StrictMath
メソッド。
public static int absExact(int a)
public static long absExact(long a)
合格した場合
Integer.MIN_VALUE
または
Long.MIN_VALUE
例外がスローされます。
https://bugs.openjdk.java.net/browse/JDK-8241805
Long.MIN_VALUEまたはInteger.MIN_VALUEのいずれかが渡されたかどうかを確認したいのですが、正の値が返され、例外ではありませんが。