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

2022-08-31 10:09:42

此代码:

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

返回-2147483648

它不应该将绝对值返回为 ?2147483648


答案 1

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

有关此事的更多阅读,您可能需要查看维基百科上关于Two的补充的文章


答案 2

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

如果参数不为负,则返回该参数。如果参数为负数,则返回参数的否定。

也就是说,其行为应类似于以下 Java 代码:Math.abs(int)

public static int abs(int x){
    if (x >= 0) {
        return x;
    }
    return -x;
}

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

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

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

整数值可以在Java中以十六进制形式记录(使用a或任何其他方法检查出来)。这样取取可以得到:-10xFFFFFFFFprintln-(-1)

-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1

所以,它的工作原理。

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

-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1 
                     = 0x80000000 = Integer.MIN_VALUE

这就是返回的原因。另请注意,是 。Math.abs(Integer.MIN_VALUE)Integer.MIN_VALUE0x7FFFFFFFInteger.MAX_VALUE

也就是说,我们该如何避免未来由于这种反直觉的回报值而产生的问题呢?

  • 正如@Bombe所指出的那样,我们可以把我们的s投向以前。但是,我们必须要么intlong

    • 将它们转换回 s,这不起作用,因为 .intInteger.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
    • 或者继续使用 s,希望我们永远不会以等于 的值调用,因为我们也有 。longMath.abs(long)Long.MIN_VALUEMath.abs(Long.MIN_VALUE) == Long.MIN_VALUE
  • 我们可以在任何地方使用 s,因为 BigInteger.abs() 确实总是返回一个正值。这是一个很好的替代方法,尽管比操作原始整数类型慢一些。BigInteger

  • 我们可以为 编写自己的包装器,如下所示: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);
}
  • 使用按位和的整数来清除高位,确保结果为非负:(实质上是从 到 溢出而不是int positive = value & Integer.MAX_VALUEInteger.MAX_VALUE0Integer.MIN_VALUE)

最后要注意的是,这个问题似乎已经知道了一段时间。例如,请参阅有关相应查找虫规则的此条目


推荐