【发布时间】:2013-09-02 04:00:34
【问题描述】:
当我说Integer i = Math.abs(Integer.MIN_VALUE)时,在java中。我得到与答案相同的值,这意味着i 包含Integer.MIN_VALUE。
我也在 C++ 中验证了相同的内容。
为什么会有这种行为?
【问题讨论】:
当我说Integer i = Math.abs(Integer.MIN_VALUE)时,在java中。我得到与答案相同的值,这意味着i 包含Integer.MIN_VALUE。
我也在 C++ 中验证了相同的内容。
为什么会有这种行为?
【问题讨论】:
阅读 Joshua Bloch 的 Effective Java。
我找到了这个问题的答案,下面是解释:
计算机使用二进制算术,java中Math.abs或任何语言中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`
从上面的演示中,我们发现111(-1) is 001(1)的2补码,110(-2) is 010(2)的2补码,101(-3) is 011(3)的2补码和100(-4) is 100(-4)的2补码,我们可以看到-4是最小的负数可能使用 3 位。
这就是Integer.MIN_VALUE的绝对值是Integer.MIN_VALUE的原因。
【讨论】:
存在一种固有的不对称性,这是造成这种影响的根本原因。 32 位位模式的数量是偶数。其中一种模式用于零。这留下了奇数个非零值。正值的个数和负值的个数不能相等,因为它们的和是奇数。
在用于 Java 整数的 2 的补码表示中,负数的数量比正数的数量大一。除 Integer.MIN_VALUE 之外的每个负数都对应一个正数,该正数既是它的负数,也是它的绝对值。剩下的 Integer.MIN_VALUE,没有对应的正整数。 Math.abs 和 negation 将其映射到自身。
【讨论】:
为什么会有这种行为?
这是所有现代计算机中使用的表示选择的数学结果。
这是一个非正式的证明,说明为什么会这样。
N 位的有符号二进制数表示有 2N 个可能的值;即具有偶数个元素的集合整数。
从集合中删除零。该集合现在具有奇数个元素。
现在删除所有 对 形式的 {n, -n} 数字。每次我们删除一对数字时,集合仍然包含奇数个元素。
我们现在剩下一个包含奇数个整数的集合,n 在集合中,但-n 不在集合中。由于集合大小是奇数,所以不能为空;即此属性有至少一个数字。
在 Java 指定的表示形式中(实际上也使用了所有其他实用语言),整数类型有 2N-1 个负值和 2N-1 - 1 个值大于零。具有奇怪属性的值是MIN_VALUE。这种表示称为two's complement。
严格来说,整数表示不一定有这个异常:
可以有两个零(-0 和 +0);例如signed magnitude 或 one's complement 表示。 (这使证明的第 2 步无效:现在有 2 个零要删除。)
可以排除-2N-1;即使其成为非法值。 (这使证明的第 1 步无效:初始集合现在具有奇数个值。)
可以指定整数运算,以便(例如)否定 MIN_VALUE 引发异常。例如Math.abs(Integer.MIN_VALUE) 会抛出异常。
但是,所有这些都会对性能产生重大影响,尤其是因为现代计算机硬件本身仅支持二进制补码算法。他们在编写可靠的整数代码方面也存在问题......
【讨论】:
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
【讨论】:
您可能期望Integer.MIN_VALUE 的绝对值是Integer.MAX_VALUE + 1,但这个值超出了原始int 大小。而Integer.MAX_VALUE + 1 又是Integer.MIN_VALUE。就是这样。
【讨论】:
Integer.MIN_VALUE 和Integer.MAX_VALUE 是彼此的补码,在二进制补码系统。
Integer.MIN_VALUE 和 Integer.MAX_VALUE 并不是真正的补充(除非你重新说的是它们是最小值和最大值)。由于您可以用 2 的补码形式表示比正数多 1 个负数,因此 Integer.MIN_VALUE 的 幅度 比 Integer.MAX_VALUE 大一。
Integer.MIN_VALUE 和Integer.MAX_VALUE 是ones'-互补。
这是因为二进制补码系统的工作方式。 Integer.MIN_VALUE 对应 0x80000000。否定它的标准方法是取其补码(在这种情况下为 0x7FFFFFFF)并加 1,在这种情况下它将溢出回 0x80000000。
【讨论】: