我有一种 int 和一种 uint 类型:
int tempA = 0xbc000669;
uint tempB = 0xbc000669;
当我做
tempA /0x2000
,想向右移动 13,它给了我0xfffde001
尽管
tempB / 0x2000
,也想向右移动 13,它给了我正确的答案:0x0005e000
任何人都知道为什么有一个签名的部门,我得到一个错误的答案?我没想到除法会溢出?谢谢
在声明中
tempA / 0x2000
编译器会看到一个 int 类型的变量和一个数字文字值。由于 int 类型的除法需要两个 int 类型的操作数,因此值 0x2000 也会自动转换为 int。该语句评估为
(int)0xbc000669 / (int)0x2000
即 -1140849047 / 8192 等于 -139263
十六进制的 -139263 是 FFFDE001(至少在 32 位值上)
0xbc000669
实际上不能包含在 a 中int
,更准确地说它将代表 a negative integer
。我们知道anint
使用32nd bit
. sign bit
While0cbc000669
具有最重要的半字节,因为0xb
它等于1011
-> 32nd bit
is1
并且实际整数是负数。所以:
int tempA = 0xbc000669;
将tempA
等于-1140849047
,将此数字右移 13 位二进制数字将返回您得到的确切结果:0xfffde001
.
如果你这样声明你tempB
的uint
:
uint tempB = 0xbc000669;
它可以完全包含在 a 中,uint
因为uint
不使用 the32nd bit
作为符号位。实际数字是 a positive integer
,它的值是3154118249
,将此值右移 13 位二进制数字将为您提供准确的结果0x5e000
解释这一点的最好方法是展示这些位是如何工作的。 关于签名数字表示的维基百科文章很好地涵盖了它。您需要阅读关于二进制补码的部分。
我认为,人们希望 -1 和 1 的二进制值相同,只是符号位是 1 表示 -1。不是这种情况。例如,1 是
0000 0000 0000 0000 0000 0000 0000 0001
-1 是
1111 1111 1111 1111 1111 1111 1111 1111
人们直觉上会认为-1实际上是-127:
-127 == 1000 0000 0000 0000 0000 0000 0000 0001
那么现在让我们看看你得到的答案。在有符号除法中,当您向右移动时,您实际上最终会用 1 填充数字的左侧,然后加 1。这正是您在结果中看到的。
0xFFFDE001 = 1111 1111 1111 1101 1110 0000 0000 0001
0x0005E000 = 0000 0000 0000 0101 1110 0000 0000 0000
正如您会注意到的,它们都是相同的,除了顶部的(来自您签名的部门)在左侧填充了 13 个 1,并在末尾添加了 1。
有符号整数要记住的重要一点是二进制补码会改变位的顺序,因此,不包括符号位,-50 与 +50 的位模式不同。
从规范中关于整数除法的简介:
除法将结果向零舍入,结果的绝对值是小于两个操作数的商的绝对值的最大可能整数。当两个操作数具有相同符号时,结果为零或正,当两个操作数具有相反符号时,结果为零或负。
如果我正确解释该文本,则会导致:在第一个示例中,有符号的 int 值设置了符号位;第二个数字 (0x2000) 为正数,因此结果为零或负数。