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

发布于 2024-10-26 05:33:51 字数 167 浏览 8 评论 0原文

此代码:

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

返回 -2147483648

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

This code:

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

Returns -2147483648

Should it not return the absolute value as 2147483648 ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

半岛未凉 2024-11-02 05:33:51

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

要进一步阅读有关此问题的信息,您可能需要查看有关二进制补码的维基百科文章。

Integer.MIN_VALUE is -2147483648, but the highest value a 32 bit integer can contain is +2147483647. Attempting to represent +2147483648 in a 32 bit int will effectively "roll over" to -2147483648. This is because, when using signed integers, the two's complement binary representations of +2147483648 and -2147483648 are identical. This is not a problem, however, as +2147483648 is considered out of range.

For a little more reading on this matter, you might want to check out the Wikipedia article on Two's complement.

睫毛溺水了 2024-11-02 05:33:51

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

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

也就是说,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 在 Java 中可以用十六进制表示为 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。另请注意,0x7FFFFFFFInteger.MAX_VALUE

那么,我们如何避免未来由于这种反直觉的返回值而出现问题呢?

  • 我们可以,正如指出的那样@Bombe,将我们的 int 转换为 long 。然而,我们必须要么

    • 将它们转换回 int,这不起作用,因为
      Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
    • 或者以某种方式继续使用 long,希望我们永远不会使用等于 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);
}
  • 使用整数按位与清除高位,确保结果非负:int Positive = value & Integer.MAX_VALUE(本质上是从 Integer.MAX_VALUE 溢出到 0,而不是 Integer.MIN_VALUE

最后要注意的是,此问题似乎认识有一段时间了。例如,请参阅有关相应 findbugs 规则的此条目

The behaviour you point out is indeed, counter-intuitive. However, this behaviour is the one specified by the javadoc for Math.abs(int):

If the argument is not negative, the argument is returned.
If the argument is negative, the negation of the argument is returned.

That is, Math.abs(int) should behave like the following Java code:

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

That is, in the negative case, -x.

According to the JLS section 15.15.4, the -x is equal to (~x)+1, where ~ is the bitwise complement operator.

To check whether this sounds right, let's take -1 as example.

The integer value -1 is can be noted as 0xFFFFFFFF in hexadecimal in Java (check this out with a println or any other method). Taking -(-1) thus gives:

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

So, it works.

Let us try now with Integer.MIN_VALUE . Knowing that the lowest integer can be represented by 0x80000000, that is, the first bit set to 1 and the 31 remaining bits set to 0, we have:

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

And this is why Math.abs(Integer.MIN_VALUE) returns Integer.MIN_VALUE. Also note that 0x7FFFFFFF is Integer.MAX_VALUE.

That said, how can we avoid problems due to this counter-intuitive return value in the future?

  • We could, as pointed out by @Bombe, cast our ints to long before. We, however, must either

    • cast them back into ints, which does not work because
      Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE).
    • Or continue with longs somehow hoping that we'll never call Math.abs(long) with a value equal to Long.MIN_VALUE, since we also have Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE.
  • We can use BigIntegers everywhere, because BigInteger.abs() does indeed always return a positive value. This is a good alternative, though a bit slower than manipulating raw integer types.

  • We can write our own wrapper for Math.abs(int), like this:

/**
 * 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);
}
  • Use a integer bitwise AND to clear the high bit, ensuring that the result is non-negative: int positive = value & Integer.MAX_VALUE (essentially overflowing from Integer.MAX_VALUE to 0 instead of Integer.MIN_VALUE)

As a final note, this problem seems to be known for some time. See for example this entry about the corresponding findbugs rule.

箹锭⒈辈孓 2024-11-02 05:33:51

以下是 Java 文档在 javadoc

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

Here is what Java doc says for Math.abs() in javadoc:

Note that if the argument is equal to
the value of Integer.MIN_VALUE, the
most negative representable int value,
the result is that same value, which
is negative.

北渚 2024-11-02 05:33:51

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

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

To see the result that you are expecting, cast Integer.MIN_VALUE to long:

System.out.println(Math.abs((long) Integer.MIN_VALUE));
风吹雪碎 2024-11-02 05:33:51

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 是否传递正值将返回而不是异常。

There is a fix to this in Java 15 will be a method to int and long. They will be present on the classes

java.lang.Math and java.lang.StrictMath

The methods.

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

If you pass

Integer.MIN_VALUE

OR

Long.MIN_VALUE

A Exception is thrown.

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

I would like to see if either Long.MIN_VALUE or Integer.MIN_VALUE is passed a positive value would be return and not a exception but.

溺ぐ爱和你が 2024-11-02 05:33:51

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

2147483648 cannot be stored in an integer in java, its binary representation is the same as -2147483648.

你不是我要的菜∠ 2024-11-02 05:33:51

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

But (int) 2147483648L == -2147483648 There is one negative number which has no positive equivalent so there is not positive value for it. You will see the same behaviour with Long.MAX_VALUE.

我恋#小黄人 2024-11-02 05:33:51

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

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

Math.abs doesn't work all the time with big numbers I use this little code logic that I learnt when I was 7 years old!

if(Num < 0){
  Num = -(Num);
} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文