Math.absがInteger.Min_VALUEに間違った値を返す


90

このコード:

System.out.println(Math.abs(Integer.MIN_VALUE));

戻り値 -2147483648

絶対値をとして返すべきではありません2147483648か?

回答:


102

Integer.MIN_VALUE-2147483648ですが、32ビット整数に含めることができる最大値は+2147483647です。+214748364832ビット整数で表現しようとすると、効果的にに「ロールオーバー」し-2147483648ます。符号付き整数を使用する場合、の2の補数バイナリ表現からである+2147483648とは-2147483648同一です。ただし、+2147483648範囲外と見なされるため、これは問題ではありません。

この問題についてもう少し詳しく知りたい場合は、2の補数に関するウィキペディアの記事を確認してください。


6
まあ、問題が影響を過小評価しているわけではありません、それは問題を意味する可能性が非常に高いです。個人的には、より高いレベルの言語で動的に成長する例外または記数法が必要です。
Maarten Bodewes 2012

40

あなたが指摘する行動は、確かに直感に反しています。ただし、この動作は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を取り上げましょう。

整数値-10xFFFFFFFF、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です。また、それ0x7FFFFFFFInteger.MAX_VALUEです。

とはいえ、将来この直感に反する戻り値による問題をどのように回避できるでしょうか。

  • @Bombeが指摘しているように、以前にintsをキャストすることができましたlong。しかし、私たちはどちらかでなければなりません

    • それらをintsにキャストし直しますが、これは機能しないため Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)です。
    • または、もあるので、に等しい値でlong呼び出さMath.abs(long)れないことを期待して、sを続行します。Long.MIN_VALUEMath.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);
}
  • 整数のビット単位のANDを使用して上位ビットをクリアし、結果が負でないことを確認します:(int positive = value & Integer.MAX_VALUE基本的にからでInteger.MAX_VALUE0なくからにオーバーフローしますInteger.MIN_VALUE

最後に、この問題はしばらくの間知られているようです。たとえば、対応するfindbugsルールについてはこのエントリを参照してください。


12

javadocのMath.abs()についてJavadocが言っていることは次のとおりです。

引数が最も負の表現可能なint値であるInteger.MIN_VALUEの値と等しい場合、結果は同じ値であり、負であることに注意してください。


4

期待する結果を確認するには、次のコマンドにキャストInteger.MIN_VALUElongます。

System.out.println(Math.abs((long) Integer.MIN_VALUE));

1
確かに、可能な修正です!ただし、これはMath.abs負の数を返すことによって直感に反しているという事実を解決しませんMath.abs(Long.MIN_VALUE) == Long.MIN_VALUE
。– bernard paulus 2013

1
@bernardpaulus、まあ、それは何をすることになっていArithmeticExceptionますか?また、動作はAPIドキュメントに明確に文書化されています。
ボンベ

あなたの質問に対する良い答えはありません...バグの原因であるこの振る舞いは、の使用によって修正されないことを指摘したいと思いますMath.abs(long)。ここでの私の間違いをお詫びしますMath.abs(long)。「アスカーが期待する結果を確認する」ための簡単な方法としてそれを示したときに、修正としての使用を提案したと思いました。ごめんなさい。
bernard paulus 2013

新しいメソッドを使用するJava15では、実際には例外がスローされます。
chiperortiz

1

2147483648はjavaに整数で格納できません。そのバイナリ表現は、-2147483648と同じです。


0

しかし(int) 2147483648L == -2147483648 、正の等価物がない負の数が1つあるため、正の値はありません。Long.MAX_VALUEでも同じ動作が見られます。


0

これに対する修正が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のいずれかが渡されたかどうかを確認したいのですが、正の値が返され、例外ではありませんが。


-1

Math.absは、大きな数では常に機能するわけではありません。7歳のときに学んだこの小さなコードロジックを使用しています。

if(Num < 0){
  Num = -(Num);
} 

sここは何ですか?
aioobe

申し訳ありませんが、元のコードから更新するのを忘れました
Dave

では、スニペットの前にNum等しい場合、これはどうなるInteger.MIN_VALUEでしょうか?
aioobe
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.