【问题标题】:why Integer.MAX_VALUE + 1 == Integer.MIN_VALUE?为什么 Integer.MAX_VALUE + 1 == Integer.MIN_VALUE?
【发布时间】:2012-03-12 22:32:10
【问题描述】:

System.out.println(Integer.MAX_VALUE + 1 == Integer.MIN_VALUE);

是真的。

我知道 Java 中的整数是 32 位的,不能超过 231-1,但我不明白为什么在其 MAX_VALUE 上加 1 会导致 MIN_VALUE 和不是某种例外。更不用说像 Ruby 那样将透明转换为更大的类型。

此行为是否在某处指定?我可以依赖它吗?

【问题讨论】:

  • 现在这里有很多很好的答案,但是我现在很好奇你为什么问“我可以依靠它吗?” - 为什么要依赖它?
  • 这可能是另一个问题,---我正在寻找一些“迭代器”,它会从元素 N 开始迭代数组,然后在到达结束元素后开始从元素 0 迭代到元素 N- 1 --- 这种整数行为可能对此有用......但我最终只有两个不同的循环
  • 仅供参考,如果你想获取异常,一些库提供了方法:GuavaApache
  • 更令人困惑的是为什么INT最小值的反转仍然是负数! int b = -Integer.MIN_VALUE;!! 2 的补码数学太混乱了。
  • 所有这些答案,都解决了 int 的工作原理,但没有一个回答这个问题:它为什么这样做?为什么不抛出异常?允许 Integer.MAX_VALUE + 1 == -2147483648 是否有一些有用的功能?

标签: java integer integer-overflow twos-complement jls


【解决方案1】:

因为整数溢出。当它溢出时,下一个值为Integer.MIN_VALUERelevant JLS

如果整数加法溢出,则结果是数学和的低位,以一些足够大的二进制补码格式表示。如果发生溢出,则结果的符号与两个操作数值的数学和的符号不同。

【讨论】:

    【解决方案2】:

    整数存储溢出,that is not indicated in any way, as stated in JSL 3rd Ed.:

    内置整数运算符不会以任何方式指示上溢或下溢。如果需要对空引用进行拆箱转换(§5.1.8),整数运算符可以抛出NullPointerException。除此之外,唯一可以抛出异常 (§11) 的整数运算符是整数除法运算符 / (§15.17.2) 和整数余数运算符 % (§15.17.3),如果右手边会抛出 ArithmeticException操作数为零,并且递增和递减运算符++(§15.15.1, §15.15.2) 和--(§15.14.3, §15.14.2),如果装箱转换(§5.1.7) 是,则可以抛出OutOfMemoryError需要,并且没有足够的可用内存来执行转换。

    4 位存储示例:

    MAX_INT: 0111 (7)
    MIN_INT: 1000 (-8)
    

    MAX_INT + 1:

     0111+
     0001
     ----
     1000
    

    【讨论】:

    • 所有这些答案,都解决了 int 的工作原理,但没有一个回答这个问题:它为什么这样做?为什么它不抛出异常?允许 Integer.MAX_VALUE + 1 == -2147483648 是否有一些有用的功能?
    【解决方案3】:

    您必须了解整数值如何以二进制形式表示,以及二进制加法的工作原理。 Java 使用一种称为二进制补码的表示,其中数字的第一位代表它的符号。每当你给最大的java Integer加上1,它的位符号为0,那么它的位符号就变成1,数字变成负数。

    此链接更详细地解释:http://www.cs.grinnell.edu/~rebelsky/Espresso/Readings/binary.html#integers-in-java

    --

    Java 语言规范在此处处理此行为:http://docs.oracle.com/javase/specs/jls/se6/html/expressions.html#15.18.2

    如果整数加法溢出,则结果是数学和的低位,以一些足够大的二进制补码格式表示。如果发生溢出,则结果的符号与两个操作数值的数学和的符号不同。

    这意味着您可以依赖这种行为。

    【讨论】:

      【解决方案4】:

      在大多数处理器上,算术指令没有发生溢出故障的模式。他们设置了一个必须检查的标志。这是一个额外的指令,所以可能会更慢。为了使语言实现尽可能快,经常指定语言以忽略错误并继续。对于 Java,行为在 JLS 中指定。对于 C,语言没有指定行为,但现代处理器的行为与 Java 相同。

      我相信有人建议(尴尬的)Java SE 8 库在溢出时抛出,以及未签名的操作。我相信在 DSP 世界中流行的一种行为是将值限制在最大值,所以 Integer.MAX_VALUE + 1 == Integer.MAX_VALUE [不是 Java]。

      我确信未来的语言会使用任意精度的整数,但暂时不会。需要更昂贵的编译器设计才能快速运行。

      【讨论】:

      • 这是唯一正确回答实际问题的答案。
      【解决方案5】:

      当您越过国际日期变更线时,日期发生变化的原因与此相同:那里存在不连续性。它内置于二进制加法的本质中。

      【讨论】:

      • 确实存在不连续性(或者换句话说,在int 的范围内)并且必须以某种方式处理。但这并不意味着它必须以这种方式处理。还有许多其他方法可以处理整数溢出……至少在理论上是这样。
      【解决方案6】:

      这是一个众所周知的问题,与整数在二进制层向下表示为 two's complement 的事实有关。当您将 1 添加到二进制补码的最大值时,您会得到最小值。老实说,在 java 存在之前,所有整数都是这样表现的,而改变 Java 语言的这种行为会增加整数数学的开销,并使来自其他语言的程序员感到困惑。

      【讨论】:

        【解决方案7】:

        当你将3(二进制11)加到1(二进制1)时,你必须从右边开始改为0(二进制0)所有二进制1,直到你得到 0,你应该把它改为1Integer.MAX_VALUE 的所有位置都被1 填满,所以只剩下0s。

        【讨论】:

          【解决方案8】:

          通过字节示例易于理解=>

          byte a=127;//max value for byte
          byte b=1;
          
          byte c=(byte) (a+b);//assigns -128
          System.out.println(c);//prints -128
          

          这里我们强制加法并将其转换为字节。 所以会发生的是,当我们达到 127(一个字节的最大可能值)并且我们加 1 时,该值从 127 翻转(如图所示)并变为 -128。 值开始围绕类型循环。

          整数也一样。

          integer + integer 也保持整数(与 byte + byte 不同,后者被转换为 int [除非如上所述强制转换])。

          int int1=Integer.MAX_VALUE+1;
          System.out.println(int1); //prints -2147483648
          System.out.println(Integer.MIN_VALUE); //prints -2147483648
          
          //below prints 128 as converted to int as not forced with casting
          System.out.println(Byte.MAX_VALUE+1);
          

          【讨论】:

          • 关于添加byte 算术的东西有点误导......而且并不真正相关。由于byte 值在对其执行任何算术运算之前总是被提升为int(或“更大”类型),因此讨论涉及byte 的示例对理解整数溢出行为没有帮助。
          【解决方案9】:

          由于溢出和符合双重标准的自然计数进入“第二个循环”,我们在最右边的位置 2147483647 和 1 后,我们出现在最左边的位置 -2147483648,下一个递增去 -2147483647,-2147483646, -2147483645, ... 以此类推,一直到最右边,一直如此,它在此位深度上求和机的性质。

          一些例子:

          int a = 2147483647;
          
          System.out.println(a);
          

          提供:2147483647

          System.out.println(a+1);
          

          给出:-2147483648(导致溢出和符合双重标准的自然计数进入“第二个循环”,我们位于最右边的位置 2147483647 并且在求和 1 后,我们出现在最左边的位置 -2147483648,下一个递增 - 2147483648, -2147483647, -2147483646, ... 等等,一直到最右边,一直在,它在这个位深度上的求和机的性质)

          System.out.println(2-a); 
          

          给出:-2147483645(-2147483647+2 似乎是数学逻辑)

          System.out.println(-2-a);
          

          给出:2147483647(-2147483647-1 -> -2147483648,-2147483648-1 -> 2147483647 之前的答案中描述的一些循环)

          System.out.println(2*a);
          

          给出:-2(2147483647+2147483647 -> -2147483648+2147483646 再次数学逻辑)

          System.out.println(4*a);
          

          给出:-4(2147483647+2147483647+2147483647+2147483647->-2147483648+2147483646+2147483647+2147483647->-2-2(根据最后一个答案)->-4)`

          【讨论】:

            猜你喜欢
            • 2016-07-19
            • 1970-01-01
            • 1970-01-01
            • 2013-09-24
            • 1970-01-01
            • 1970-01-01
            • 2015-11-03
            • 1970-01-01
            • 2012-10-13
            相关资源
            最近更新 更多