原码、反码、补码

                     

贡献者: lzq; addis

1. 原码(True form)

   原码即 “未经更改” 的码,是指一个二进制数左边加上符号位后所得到的码,且当二进制数大于 $0$ 时,符号位为 0;二进制数小于 $0$ 时,符号位为 $1$;二进制数等于 $0$ 时,符号位可以为 $0$ 或 $1$(表示 $+0$ 或 $-0$)。

   使用 $n$ 位原码表示有符号数时,范围是 $-(2^{n-1}-1)$ 到 $+(2^{n-1}-1)$。当 $n=8$ 时,第一位用于符号位,这个范围就是 $-127\sim +127 $;表示无符号数时,由于不需要考虑数的正负,就不需要用一位来表示符号位,$n$ 位机器数全部用来表示是数值,这时表示数的范围就是 $0\sim 2^{n}-1$。当 $n=8$ 时,这个范围就是 $0\sim 255$。

   优点: 简单直观,原码易于人类理解和计算(与真值转换容易)。

   缺点:原码不能用无符号的加法器进行运算。例如,数学上,$1+(-1)=0$,但把原码直接进行无符号的加法运算时(即把两个相加的数都视为 $0-255$ 的整数相加):

\begin{equation} 00000001 + 10000001=10000010~. \end{equation}
该结果对应数值为 $-2$。显然出错了; 对于减法运算,原码减法需要先将减数取反加 $1$,才能得到正确的数学结果。

   也就是说:原码的运算,必须将符号位和其他位分开,这就增加了硬件的开销和复杂性。也有人将该符号问题称作正负 $0$ 现象。

2. 反码(1's complement)

   我们发现:原码最大的问题就在于一个数加上它的相反数不等于 $0$,于是反码的设计思想就是冲着解决这一点,既然一个负数是一个正数的相反数,那干脆用一个正数按位取反来表示负数。

   在反码表示法中,正数和 $0$ 的反码与其原码相同;负数的反码则是将原码(除符号位外)的每一位取反。

   缺点: 虽然解决了相反数相加不等于 $0$ 的问题,但是反码不能直接做减法,并且存在多余的负零(eg. 1111_1111)

例 1 

   0001+1110=1111,1+(-1)=-0;

   1110+1100=1010,(-1)+(-3)=-5。

3. 补码(2's complement)

   正数和 $0$ 的补码就是该数字本身再补上最高比特 $0$。负数的补码则是将其绝对值按位取反再加 $1$。

   优点: 在实现加法器时,只要一种加法电路就可以处理各种有号数加法,因为减法可以用一个数加上另一个数的补码来表示,因此只要有加法电路及补码电路即可完成各种有号数加法及减法,在电路设计上相当方便。(实际在加法器做减法运算时,只需要让减数通过非门,并让个位的 “进位” 端口从 $0$ 变为 $1$ 即可)

   另外,补码系统的 $0$ 就只有一个表示方式,这和反码系统不同(在反码系统中,$0$ 有两种表示方式),因此在判断数字是否为 $0$ 时,只要比较一次即可。

4. 补码的设计

   补码的规则看起来很怪,我们可以换一种角度理解:

   在设计一个编码时,我们可以把编码后的数围成一个圆(类似一个时钟),我们希望在这个钟表上:

  1. 无冲突,每个数值的位置都是独一无二,有唯一的 $0$。
  2. 连续性,每次运算($+1$)相当于时钟顺时针移动一个单位,尤其是从最大的正数加 $1$ 会 “溢出” 到最小的负数,这种看似异常的行为实际上提供了一个连续且循环的数值范围,使得算术运算能够在这个编码系统中顺畅地进行。

   从上述角度看,补码是一种很和谐的编码。

5. 补码的总结

   对 $N$ bit 的有符号整数,如果用补码表示负整数,则:

                     

© 小时科技 保留一切权利