【问题标题】:Why is absolute of Integer.MIN_VALUE equivalent to Integer.MIN_VALUE为什么 Integer.MIN_VALUE 的绝对值等于 Integer.MIN_VALUE
【发布时间】:2013-09-02 04:00:34
【问题描述】:

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

为什么会有这种行为?

【问题讨论】:

    标签: java integer


    【解决方案1】:

    阅读 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的原因。

    【讨论】:

    • s/compliment/complement/g. Java 根据定义使用二进制补码,但 C++ 实现不需要使用二进制补码。如果它使用了一个的补码,则上述内容将不适用。
    • @ChrisJester-Young 感谢您提出编辑建议。 s/恭维/补充/g。为时已晚,但值得。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      为什么会有这种行为?

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

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

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

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

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

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

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


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

      • 可以有两个零(-0 和 +0);例如signed magnitudeone's complement 表示。 (这使证明的第 2 步无效:现在有 2 个零要删除。)

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

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

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

      【讨论】:

        【解决方案4】:

        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

        【讨论】:

        • 特别感谢您对 Java 语言规范的具体参考。
        【解决方案5】:

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

        【讨论】:

        • 为什么会有这种特殊的处理方式,即用 Integer.MIN_VALUE 加 1 而不是其他整数?
        • @dharam 这不是特殊处理,我试图解释 Integer.MIN_VALUE 的绝对值大于可以通过 int 类型表示的最大整数。
        • @dharam 在二进制补码算术中,您通过取其一个补码并加 1 来否定一个数字。碰巧Integer.MIN_VALUEInteger.MAX_VALUE 是彼此的补码,在二进制补码系统。
        • @ChrisJester-Young - 我不确定你在做什么(因为你的实际答案看起来是正确的),但 Integer.MIN_VALUEInteger.MAX_VALUE 并不是真正的补充(除非你重新说的是它们是最小值和最大值)。由于您可以用 2 的补码形式表示比正数多 1 个负数,因此 Integer.MIN_VALUE幅度Integer.MAX_VALUE 大一。
        • @DaoWen 我的意思是Integer.MIN_VALUEInteger.MAX_VALUEones'-互补。
        【解决方案6】:

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

        【讨论】:

          猜你喜欢
          • 2015-11-03
          • 2012-03-12
          • 2022-11-16
          • 2011-07-23
          • 2015-10-10
          • 1970-01-01
          • 2017-04-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多