曾在很多书籍中看到过关于原码、反码和补码的介绍,都未能够深入理解。今在Richard Blum编写的《汇编语言程序设计》一书中读到关于带符号整数一节的讲解时(确切的说是其中的一句话),的确有茅塞顿开、恍然大悟之感。原文如下:
7.2.3 带符号整数
虽然使用无符号整数很容易,但是其缺陷是没有办法表示负数。为了解决这个问题,在处理器上需要采用能够表示负数的方法。有3种方法用于在计算机中描述负数:
(1)带符号数值(其他书籍中所谓的原码)
(2)反码(One's complement)
(3)补码(Two's complement)
所有这3种方法都使用和无符号整数相同的位长度(字节8、字16、双字32和四字64),但是在位中表示十进制值的方式是不同的。IA-32平台使用补码方式表示带符号整数,但是了解每种方法如何工作是有好处的。下面几节介绍每种方法。
1、带符号数值
带符号数值的方法把组成带符号整数的位分为两部分:符号位和数值位。字节的最大有效位(最左侧的一位)用于表示值的符号。正数的最大有效位包含0,而负数的这个位置是1。值中的其余位使用一般的二进制值表示数字的数值。
带符号数值的一个问题是有两种不同的表示0值的方式:00000000(十进制的+0)和10000000(十进制的-0)。这使一些数学处理变得复杂,因为简单的带符号整数的加法和减法不能按照无符号数字的方式进行。例如,简单的加法00000001(十进制1)和10000001(十进制-1)相加得到10000010(十进制-2),这不是正确的答案。这要求处理器为带符号整数和无符号整数提供不同的数学运算指令。
2、反码
反码方法采用无符号整数的相反代码生成相应的负值。求反把所有为0的位改变为1,把所有为1的位改变为0。因此,00000001的反码就是11111110。同样,对于带符号数字,当执行数学操作时,反码数字会产生一些问题。有两种方式可以表示零值(00000000和11111111),反码数字的数学运算也是复杂的(它不允许进行标准二进制运算)。
3、补码
补码通过使用简单的数学技巧,解决了带符号数值和反码方法的数学运算问题。对于负整数值,值的反码加上1就是它的补码。
例如,为了得到十进制-1的补码,可以这样做:
1)得到00000001的反码,结果是11111110.
2)反码加上1,结果是11111111.
对-2值进行相同的处理,会得到11111110,-3会得到11111101。读者也许会注意到这里的规律。补码值从11111111(十进制的-1)开始递减,直到到达10000000,它表示-128。当然,对于多字节整数长度,相同的规则跨字节使用。
虽然这样做看上去有些奇怪,但是它解决了带符号整数的加法和减法的所有问题。例如值00000001(+1)和11111111(-1)相加得到00000000,并且带有进位值。在整数运算中进位值被忽略,所以最终的值确实是0。相同的硬件可以同时用于无符号值和带符号值的加法和减法操作。