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
。 另请注意, 0x7FFFFFFF
是Integer.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相同的行为。