100

这段代码:

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

退货-2147483648

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

4

8 回答 8

114

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

要进一步了解这个问题,您可能需要查看关于二进制补码的 Wikipedia 文章

于 2011-03-26T19:11:47.077 回答
44

您指出的行为确实是违反直觉的。但是,此行为是javadocMath.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,其中~是按位补码运算符。

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

整数值-1可以用0xFFFFFFFFJava 中的十六进制表示(用一种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.

也就是说,我们如何才能避免由于这种违反直觉的返回值而导致的问题呢?

  • 正如@Bombe 所指出的,我们可以将我们int的 s 投射到long之前。然而,我们必须要么

    • 将它们重新转换为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);
}
  • 使用整数位 AND 清除高位,确保结果为非负数:(本质上是从to而不是int positive = value & Integer.MAX_VALUE溢出)Integer.MAX_VALUE0Integer.MIN_VALUE

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

于 2013-06-22T23:01:57.687 回答
13

以下是 Java 文档对javadoc中 Math.abs() 的说明:

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

于 2011-03-26T19:13:15.590 回答
6

要查看您期望的结果,请Integer.MIN_VALUE转换为long

System.out.println(Math.abs((long) Integer.MIN_VALUE));
于 2011-03-26T19:16:05.667 回答
1

2147483648在java中不能以整数形式存储,其二进制表示与-2147483648相同。

于 2011-03-26T19:14:35.963 回答
1

Java 15 对此有一个修复,它将是一个 int 和 long 的方法。他们将出现在课堂上

java.lang.Math and java.lang.StrictMath

方法。

public static int absExact(int a)
public static long absExact(long a)

如果你通过

Integer.MIN_VALUE

或者

Long.MIN_VALUE

抛出异常。

https://bugs.openjdk.java.net/browse/JDK-8241805

我想看看是否传递了 Long.MIN_VALUE 或 Integer.MIN_VALUE 一个正值将被返回,而不是异常。

于 2020-04-20T14:54:07.417 回答
0

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

于 2011-03-26T19:15:46.700 回答
-3

Math.abs 并不总是适用于大数字 我使用我在 7 岁时学到的这个小代码逻辑!

if(Num < 0){
  Num = -(Num);
} 
于 2019-11-11T21:51:18.857 回答