两种解释整数溢出的方法
Posted
技术标签:
【中文标题】两种解释整数溢出的方法【英文标题】:Two Ways to Interpret Integer Overflow 【发布时间】:2015-01-04 05:02:17 【问题描述】:我在这里(https://***.com/a/27762490/4415632)读到,当整数溢出发生时,最高有效位被简单地切断。
不过,我在这里也读到过(https://***.com/a/27747180/3808877),当溢出发生时,“值变成了该类型的最小值,重新开始向上计数”。哪一个是正确的,还是两个答案都正确?如果是这样,谁能告诉我为什么这两种解释彼此等价?
【问题讨论】:
【参考方案1】:两者都是正确的,这取决于上下文。一种是强制转换的结果,一种是溢出的结果。这些是不同的操作。例如,如果您将 Long.MAX_VALUE
转换为 int
,这是一个转换操作
System.out.println((int) Long.MAX_VALUE); // <-- -1
如果您通过在Integer.MAX_VALUE
上加一来溢出int
,那么
System.out.println(Integer.MAX_VALUE + 1); // <-- Integer.MIN_VALUE
【讨论】:
出于好奇,这两种方法的结果是否相同?我是第二个问题的回答者之一,惊讶地发现接受答案的二进制解释给出了与我编写的小算法相同的结果 两种方法给出相同的结果,因为这两种方法实际上是相同的东西。 其实我认为这对于 MSB 来说是不正确的【参考方案2】:两种解释都是正确的,因为它们实际上是相同的。
让我们看看数学,看看为什么。
Java 将值存储在 byte
、short
、char
、int
和 long
中,格式为 two's complement。
对于byte
、short
、int
和long
,它是签名的,对于char
,它是未签名的。
二的补码 格式的属性之一是,对于大多数操作,将值解释为 signed 还是 unsigned 都无关紧要。生成的位模式将是相同的。
为了简短起见,我将使用byte
进行解释,但其他类型的工作原理相同。
byte
有 8 位。最高位被解释为 sign 位。所以,位模式是这样的:
snnn nnnn
分成两组,每组 4 位称为 Nibble,在这里执行是为了纯粹的可读性。作为旁注,半字节可以用十六进制数字表示。
所以一个字节中有 8 位,每个位可以是 0 或 1。这给我们留下了 2^8 = 256
可以存储在 byte
中的不同值。
以下是一些示例值:
0000 0000 -> 0
0000 0001 -> 1
0000 0010 -> 2
0100 0000 -> 64
0111 1111 -> 127
1000 0000 -> -128
1111 1110 -> -2
1111 1111 -> -1
有符号数的 2 的补码值,即符号位已设置,是通过取 8 位的正值并减去范围来创建的,即在一个字节的情况下减去 256。
现在让我们看看如果你把-1
加上1
会发生什么。
1111 1111 -1 / 255
+ 0000 0001 1
--------------
= 1 0000 0000 -0 / 256 intermediate result
= 0000 0000 0 / 256 result after dropping excess leading bits
有溢出。结果现在需要 9 位,但字节只有 8 位,所以最重要的位丢失了。
让我们看另一个例子,-1
加上-1
。
1111 1111 -1 / 255
+ 1111 1111 -1 / 255
--------------
= 1 1111 1110 -2 / 510 intermediate result
= 1111 1110 -2 / 254 result after dropping excess leading bits
或者这个,127
加上5
。
0111 1111 127
+ 0000 0101 5
--------------
= 1000 0100 132 / -124
正如我们所见,前导位被丢弃了,这实际上是导致“再次从最小值开始计数”而导致其溢出的原因。
【讨论】:
【参考方案3】:我添加了另一个选项:处理器陷阱。一些处理器会在整数溢出时产生陷阱。如果可用,通常可以通过在处理器状态寄存器中设置一个位在用户模式下启用此功能。
【讨论】:
好的,现在向我们展示如何以与 JVM 兼容的方式做到这一点。以上是关于两种解释整数溢出的方法的主要内容,如果未能解决你的问题,请参考以下文章