【发布时间】:2011-07-23 14:38:44
【问题描述】:
这段代码:
System.out.println(Math.abs(Integer.MIN_VALUE));
返回-2147483648
不应该返回绝对值2147483648吗?
【问题讨论】:
标签: java absolute-value
这段代码:
System.out.println(Math.abs(Integer.MIN_VALUE));
返回-2147483648
不应该返回绝对值2147483648吗?
【问题讨论】:
标签: java absolute-value
Integer.MIN_VALUE 是 -2147483648,但 32 位整数可以包含的最大值是 +2147483647。尝试在 32 位 int 中表示 +2147483648 将有效地“翻转”到 -2147483648。这是因为,当使用有符号整数时,+2147483648 和-2147483648 的二进制补码表示是相同的。不过,这不是问题,因为+2147483648 被认为超出范围。
如需更多了解此问题,您可能需要查看Wikipedia article on Two's complement。
【讨论】:
您指出的行为确实违反直觉。但是,此行为是由javadoc for Math.abs(int) 指定的行为:
如果参数不是负数,则返回参数。 如果参数是否定的,则返回参数的否定。
也就是说,Math.abs(int) 的行为应该类似于以下 Java 代码:
public static int abs(int x){
if (x >= 0) {
return x;
}
return -x;
}
也就是说,在否定的情况下,-x。
根据JLS section 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 的原因。还要注意0x7FFFFFFF 是Integer.MAX_VALUE。
也就是说,我们如何才能避免将来由于这种违反直觉的返回值而出现问题?
我们可以 as pointed out by @Bombe 之前将我们的 ints 转换为 long。然而,我们必须要么
ints,这不起作用,因为
Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)。longs 以某种方式希望我们永远不会使用等于Long.MIN_VALUE 的值调用Math.abs(long),因为我们也有Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE。我们可以在任何地方使用BigIntegers,因为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)最后一点,这个问题似乎已经有一段时间了。参见例如this entry about the corresponding findbugs rule。
【讨论】:
这是 Java 文档在 javadoc 中对 Math.abs() 所说的:
注意,如果参数等于 Integer.MIN_VALUE 的值, 最负的可表示 int 值, 结果是相同的值,其中 是否定的。
【讨论】:
要查看您期望的结果,请将Integer.MIN_VALUE 转换为long:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
【讨论】:
Math.abs 通过返回负数违反直觉的事实:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
ArithmeticException,它还能做什么?此外,API 文档中清楚地记录了该行为。
Math.abs(long) 无法解决。我在这里为我的错误道歉:我认为您建议使用Math.abs(long) 作为修复,当您将其展示为“查看提问者期望的结果”的简单方法时。对不起。
2147483648在java中不能存储为整数,其二进制表示与-2147483648相同。
【讨论】:
在 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 是否会返回正值,而不是异常。
【讨论】:
但是(int) 2147483648L == -2147483648 有一个负数没有正等值,所以它没有正值。您将看到 Long.MAX_VALUE 的相同行为。
【讨论】:
Math.abs 并不总是适用于大数字我使用我在 7 岁时学到的这个小代码逻辑!
if(Num < 0){
Num = -(Num);
}
【讨论】:
s 是什么?
Num在sn-p之前等于Integer.MIN_VALUE会导致什么结果呢?