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


90

这段代码:

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

退货 -2147483648

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

Answers:


102

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

有关此问题的更多信息,您可能想查看Two的补语上Wikipedia文章


6
好吧,不是有一个问题低估了影响,这很可能意味着问题。就我个人而言,我更希望有一个例外或一个数字系统,该系统以高级语言动态地增长。
Maarten Bodewes,2012年

40

您指出的行为确实是违反直觉的。但是,此行为是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为例。

在Java中,整数值-1可以用0xFFFFFFFF十六进制表示(请使用aprintln或任何其他方法进行检查)。以-(-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所指出的,我们可以将ints强制转换long以前。但是,我们必须

    • 将它们转换回ints,这是不起作用的,因为 Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
    • 或继续long某种希望永远不要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来清除高位,以确保结果为非负数:(int positive = value & Integer.MAX_VALUE本质上是从溢出Integer.MAX_VALUE0而不是Integer.MIN_VALUE

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


12

这是Java文档在javadoc中对Math.abs()的描述:

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


4

要查看您期望的结果,请强制转换Integer.MIN_VALUElong

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

1
确实可以解决!但是,这不能通过Math.abs返回负数来解决反直觉的事实:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
bernard paulus 2013年

1
@bernardpaulus,好吧,除了扔一个东西还应该做什么ArithmeticException?此外,该行为已在API文档中明确记录。
孟买2013年

您的问题没有很好的答案...我只是想指出,此行为是bug的来源,不能通过使用来解决Math.abs(long)。对于我的错误,我深表歉意:我坚信Math.abs(long),当您将其显示为“查看请求者期望的结果”的简单方法时,便建议您将其用作修复程序。抱歉。
bernard paulus

实际上,在具有新方法的Java 15中,将引发Exception。
chiperortiz

1

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


0

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


0

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,将返回一个正值,但不是异常。


-1

Math.abs不能始终使用大数字工作,我使用的是我7岁时学到的小代码逻辑!

if(Num < 0){
  Num = -(Num);
} 

这是s什么
aioobe

对不起,我忘了更新,从我原来的代码
戴夫·

那么,如果在代码段之前Num等于Integer.MIN_VALUE,这会导致什么呢?
aioobe
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.