Math.abs为Integer.Min_VALUE返回错误的值

use*_*319 77 java absolute-value

这段代码:

System.out.println(Math.abs(Integer.MIN_VALUE));
Run Code Online (Sandbox Code Playgroud)

返回 -2147483648

它不应该返回绝对值2147483648吗?

jon*_*gan 86

Integer.MIN_VALUE-2147483648,但32位整数可以包含的最高值是+2147483647.试图+2147483648用32位int表示将有效地"翻转"到-2147483648.这是因为,用符号整数时,两个二进制补码表示+2147483648-2147483648是相同的.然而,这不是问题,因为+2147483648被认为超出范围.

有关此问题的更多阅读,您可能需要查看关于Two's补充维基百科文章.

  • 嗯,不是低估了影响的问题,它很可能意味着问题.就个人而言,我宁愿有一个异常或数字系统,它以更高级别的语言动态增长. (5认同)

ber*_*lus 33

你指出的行为确实是违反直觉的.但是,此行为是由javadocMath.abs(int)指定的行为:

如果参数不是负数,则返回参数.如果参数为负数,则返回参数的否定.

也就是说,Math.abs(int)应该表现得像以下Java代码:

public static int abs(int x){
    if (x >= 0) {
        return x;
    }
    return -x;
}
Run Code Online (Sandbox Code Playgroud)

也就是说,在否定的情况下,-x.

根据JLS第15.15.4节,-x它等于(~x)+1,其中~是按位补码运算符.

要检查这听起来是否正确,让我们以-1为例.

整数值-1可以0xFFFFFFFF在Java中以十六进制表示(使用println或任何其他方法检查).以-(-1)这样得到:

-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
Run Code Online (Sandbox Code Playgroud)

所以,它的工作原理.

让我们现在尝试一下Integer.MIN_VALUE.知道最低整数可以表示0x80000000,即第一位设置为1而其余31位设置为0,我们有:

-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1 
                     = 0x80000000 = Integer.MIN_VALUE
Run Code Online (Sandbox Code Playgroud)

这就是Math.abs(Integer.MIN_VALUE)回报的原因Integer.MIN_VALUE.还要注意的0x7FFFFFFFInteger.MAX_VALUE.

也就是说,我们怎样才能避免将来由于这种反直觉的回报价值而产生的问题呢?

  • 正如@Bombe指出的那样,我们可以把它们投射intlong之前.但是,我们必须要么

    • 把它们扔回ints,这是行不通的,因为 Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE).
    • 或者继续longs以某种方式希望我们永远不会Math.abs(long)用相等的值来调用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);
}
Run Code Online (Sandbox Code Playgroud)
  • 使用整数按位AND清除高位,确保结果是非负的:( int positive = value & Integer.MAX_VALUE基本上溢出Integer.MAX_VALUE0代替Integer.MIN_VALUE)

最后一点,这个问题似乎已经有一段时间了.有关相应的findbugs规则,请参阅此条目.


moe*_*moe 11

以下是Java doc对javadoc中 Math.abs()的说法:

请注意,如果参数等于Integer.MIN_VALUE的值,即最负的可表示的int值,则结果是相同的值,即负值.


Bom*_*mbe 6

要查看您期望的结果,请转换Integer.MIN_VALUElong

System.out.println(Math.abs((long) Integer.MIN_VALUE));
Run Code Online (Sandbox Code Playgroud)