有符号数通常以二进制补码[1]的形式存储于应用程序里。维基百科介绍了各种表示方法,有兴趣的读者可参见:http://en.wikipedia.org/wiki/Signed_number_representations
。本节摘录了一些典型的单字节数值。
二进制 | 十六进制 | 无符号值 | 有符号值(补码) |
---|---|---|---|
01111111 | 0x7f | 127 | 127 |
01111110 | 0x7e | 126 | 126 |
...
|
|||
00000010 | 0x2 | 2 | 2 |
00000001 | 0x1 | 1 | 1 |
00000000 | 0x0 | 0 | 0 |
11111111 | 0xff | 253 | −1 |
11111110 | 0xfe | 254 | −2 |
...
|
|||
10000010 | 0x82 | 130 | −126 |
10000001 | 0x81 | 129 | −127 |
10000000 | 0x80 | 128 | −128 |
如果0xFFFFFFFE和0x0000002都是无符号数,则第一个数(4294967294)就比第二个数(2)大。如果这两个值表示的是有符号数,那么第一个数(−2)反而比第二个数(2)小了。所以,为了正确操作有符号数和无符号数,条件转移指令(参见本书第12章)特地分为JG/JL系列(signed型)和JA/JBE系列(unsigned型)两套指令。
为了便于记忆,我们总结其特点如下。
同一个数值即可以表示有符号数,也可以表示无符号数。
有符号数的最高数权位是符号位:1代表负数,0代表正数。
从数据宽度较小的数据转换为数据宽度较大的数据是可行的。可参见前面24.5节。
负数的补码和原码的双向转换过程是相同的,都是逐位求非再加1。这种运算过程简单易记:同一个值的正负数是相反的值,所以要求非;求非之后再加1则是因为中间的“零”占了一个数的位置。
加减运算不区分有符号数和无符号数,它们的加减运算指令完全相同。但是乘除法运算还是有区别的:在x86指令集里,有符号数的乘除指令是IMUL/IDIV,而无符号数的指令是MUL/DIV。
此外,有符号数的操作指令更多一些。例如CBW/CWD/CWDE/CDQ/CDQE(附录A.6.3)、MOVSX(15.1.1节),SAR(附录A.6.3)。
[1] two’s complement;简称为“补码”。