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

此代码:

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

返回-2147483648

它不应该返回绝对值为2147483648

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

多关于这个问题的更多的阅读,你可能想看看维基百科上有关补码的文章 。

你指出的行为确实是违反直觉的。 但是,这个行为是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 〜x (~x)+1 ,其中~是按位互补运算符。

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

在Java中,整数值-1可以用hex表示为0xFFFFFFFF (用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

也就是说,我们怎样才能避免由于这个反直觉的回报值在未来的问题?

  • 正如@庞贝所指出的那样,我们可以很早就把我们的内心投入到这个领域。 但是,我们也必须

    • 将它们转换回int s,这不起作用,因为Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
    • 或者继续以某种方式,希望我们永远不会以等于Long.MIN_VALUE的值调用Math.abs(long) ,因为我们也有Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
  • 我们可以在任何地方使用BigInteger ,因为BigInteger.abs()确实总是返回一个正值。 这是一个很好的select,比操纵原始整数types要艰难得多。

  • 我们可以为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_VALUE溢出到0而不是Integer.MIN_VALUE

作为最后一点,这个问题似乎已经有一段时间了。 请参阅有关相应findbugs规则的示例。

以下是Java文档在javadoc中对Math.abs()所说的内容:

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

要查看您所期望的结果,请将Integer.MIN_VALUE long Integer.MIN_VALUE

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

2147483648不能存储在一个整数在java中,它的二进制表示与-2147483648相同。

但是(int) 2147483648L == -2147483648有一个负数不具有正值,所以没有正值。 您将看到与Long.MAX_VALUE相同的行为。