10

在 java 中当我说Integer i = Math.abs(Integer.MIN_VALUE). 我得到与答案相同的值,这意味着i包含Integer.MIN_VALUE。我也在 C++ 中验证了相同的内容。

为什么会有这种行为?

4

6 回答 6

12

在 Joshua Bloch 的 Effective Java 中阅读此内容。

我找到了这个问题的答案,这里是解释:计算机使用二进制算术,Math.absjava 中的逻辑或absolute任何语言的函数如下所示:

if(num >= 0)
    return num;
else
    return (2's complement of the num);

注意:如何找到 2 的补码

对于给定的数字,我们首先找到它的 1 的补码,然后将其加 1。例如,考虑我们的数字是10101 1 的补码 = 01010 2 的补码 = 01011(将 1 添加到 1 的补码)

现在,为了简单明了,让我们说我们的整数(有符号)大小是 3 位,那么这里是可以使用 4 位产生的可能的数字列表:

000 --> 0 (0)
001 --> 1 (1)
010 --> 2 (2)
011 --> 3 (3) 
100 --> 4 (-4)
101 --> 5 (-3)
110 --> 6 (-2)
111 --> 7 (-1)

现在这是有符号的,这意味着一半的数字是负数,另一半是正数(负数是第一位为 1 的数字)。让我们从 开始000并尝试找到它的负数,它将是 的二进制补码000

2's complement of `000` = 1 + `111` = `000`
2's complement of `001` = 1 + `110` = `111`
2's complement of `010` = 1 + `101` = `110`
2's complement of `011` = 1 + `100` = `101`
2's complement of `100` = 1 + `011` = `100`
2's complement of `101` = 1 + `010` = `011`
2's complement of `110` = 1 + `001` = `010`  
2's complement of `111` = 1 + `000` = `001`

从上面的演示中,我们发现 的 2 的补码111(-1) is 001(1),类似地 的 2 的补码110(-2) is 010(2), 的 2 的补码101(-3) is 011(3)和 的 2 的补码,100(-4) is 100(-4)我们可以看到 -4 是使用 3 位可能的最小负数。

Integer.MIN_VALUE这就是 absolute of is的原因Integer.MIN_VALUE

于 2013-09-02T04:02:09.183 回答
5

存在一种固有的不对称性,这是这种效应的根本原因。32 位位模式的数量是偶数。其中一种模式用于零。这留下了奇数个非零值。正值的数量和负值的数量不能相等,因为它们的和是奇数。

在用于 Java 整数的 2 的补码表示中,负数的数量比正数的数量大一。除 Integer.MIN_VALUE 之外的每个负数都对应一个正数,该正数既是它的负数,也是它的绝对值。剩下的 Integer.MIN_VALUE,没有对应的正整数。Math.abs 和否定将其映射到自身。

于 2013-09-02T04:09:07.007 回答
4

为什么会有这种行为?

这是所有现代计算机中使用的表示选择的数学结果。

这是一个非正式的证明,说明为什么必须这样。

  1. 一个有 N 位的有符号二进制数表示有 2 N个可能的值;即具有偶数个元素的集合整数。

  2. 从集合中删除零。该集合现在具有奇数个元素。

  3. 现在删除表格中的所有数字{n, -n}。每次我们删除一对数字时,集合仍然包含奇数个元素。

  4. 我们现在剩下一个集合,其中包含奇数个整数,它n在集合中,但-n不在集合中。由于集合大小是奇数,所以不能为空;即这个属性至少有一个数字。

在 Java 指定的表示形式中(并且实际上使用了所有其他实用语言),整数类型有 2 N-1 个负值和 2 N-1 - 1 个大于零的值。具有奇怪属性的值是MIN_VALUE。这种表示称为二进制补码


严格来说,整数表示不一定有这个异常:

  • 可以有两个零(-0 和 +0);例如有符号的幅度一个的补码表示。(这使证明的第 2 步无效:现在有 2 个零要删除。)

  • 可以排除 -2 N-1;即使其成为非法值。(这使证明的第 1 步无效:初始集合现在具有奇数个值。)

  • 可以指定整数算术,以便(例如)否定 MIN_VALUE 引发异常。例如Math.abs(Integer.MIN_VALUE)会抛出异常。

然而,所有这些都会对性能产生重大影响,特别是因为现代计算机硬件本身只支持二进制补码算法。他们在编写可靠的整数代码方面也存在问题......

于 2013-09-02T04:37:49.590 回答
3

Math.abs(int) 文档说如果参数为负,则返回参数的否定。 JLS 15.15.4。一元减号运算符表示对于所有整数值 x,-x 等于 (~x)+1

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

于 2013-09-02T04:20:58.560 回答
2

您可能期望 的绝对值Integer.MIN_VALUEInteger.MAX_VALUE + 1,但这个值超出了原始int大小。又Integer.MAX_VALUE + 1Integer.MIN_VALUE。就这样。

于 2013-09-02T04:02:23.873 回答
1

这是因为二进制补码数字系统的工作方式。Integer.MIN_VALUE对应于 0x80000000。否定它的标准方法是取其补码(在这种情况下为 0x7FFFFFFF)并加 1,在这种情况下它将溢出回 0x80000000。

于 2013-09-02T04:02:17.463 回答