您指出的行为确实是违反直觉的。
如果参数不为负,则返回参数。 如果参数为负,则返回参数取反。
也就是说, 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可以用十六进制表示为0xFFFFFFFF (请使用println或任何其他方法进行检查)。 取-(-1)因此得出:
-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
因此,它有效。
知道最低的整数可以用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到long以前。 但是,我们必须
将它们Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)转换回int ,这不起作用,因为Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE) 。
或继续进行long s,以某种方式希望我们永远不要以等于Long.MIN_VALUE的值调用Math.abs(long) ,因为我们也有Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE 。
我们可以在任何地方使用BigInteger ,因为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_VALUE溢出为0而不是Integer.MIN_VALUE )
最后一点,这个问题似乎已经知道了一段时间。