目录
1 数值的编码
计算机是使用二进制来表示信息,因为对于电路来说,表示1和0两个状态是非常容易实现的。若要使用二进制来表示数值,则需要规定二进制对数值进行编码的规则,不同编码规则下,同一串二进制表示的数值可能不同。下面介绍几种常见的整数编码方式。
1.1 原码
原码最容易理解,对于有符号数,最高位是符号位,1表示负数,0表示正数,剩下的各个位乘其位权后相加就是对应的十进制数;对于无符号数,没有符号位。如下表:
二进制编码 | 有符号数 | 无符号数 | 二进制编码 | 有符号数 | 无符号数 |
---|---|---|---|---|---|
0000 | 0 | 0 | 1000 | 8 | -0 |
0001 | 1 | 1 | 1001 | 9 | -1 |
0010 | 2 | 2 | 1010 | 10 | -2 |
0011 | 3 | 3 | 1011 | 11 | -3 |
0100 | 4 | 4 | 1100 | 12 | -4 |
0101 | 5 | 5 | 1101 | 13 | -5 |
0110 | 6 | 6 | 1110 | 14 | -6 |
0111 | 7 | 7 | 1111 | 15 | -7 |
原码虽然简单,但是计算机并没有采用原码作为其整数编码方案,因为原码有着一些局限性:
- 对于有符号数,0的表示不唯一;
- 在二进制层面做加减运算的规则复杂,比如正数的小数减大数(0001 - 0010 = 1001),正负数之间的加减运算(0001 + 1010 = 1001),负数之间的加减运算(1001 - 1010 = 0001),这给运算电路设计带来了困难。
1.2 移码
移码可由有符号数加一个偏置常数得到,通常当编码位数为n时,这个偏置常数通常取2n−1或2n−1−1,以4位编码,bias = 24−1举例,来看一下移码的作用:
有符号数 | 移码 |
---|---|
-8 (+23) | 0000 |
-7 (+23) | 0001 |
… | … |
0 (+23) | 1000 |
… | … |
+7 (+23) | 1111 |
可见移码有如下的好处:
- 0的表示唯一;
- 大小比较可以参照无符号数的大小比较规则进行,从最高位开始,为1的大于相应位为0的,相等则看下一位。从而,比较器设计上可以复用无符号数的。
因为移码比较大小实现容易,因此移码被用来表示浮点数的阶,这样便于浮点数加减运算时的对阶(比较大小)操作。
1.3 补码
学过数字电路的应该知道,无符号的加法运算电路(加法器)是比较容易实现的。假如能够有一种编码规则,使得有符号数之间的加减运算规则可以统一到无符号数的加法运算规则(0+0=1;1+0=1;0+1=1;1+1=1,进位),那么会给硬件设计带来很大的好处。这种编码规则存在吗,怎么找到它呢?从一个简单的例子开始:10 - 4 = 6
,用加法的形式表示就是10 + (-4) = 6
,对于正数暂时采用其原码表示,看看针对负数该如何编码。
用一个表盘来表示0~2n−1个数,加1就是顺拨一格,减1就是倒拨一格,那么10 - 4 = 6
可以这么表示:
但现在我们只想做加法,不想做减法,也就是说只能顺拨,不能倒拨,即便如此仍然能够从刻度10拨到刻度6:
这样岂不是说我们用12
的编码来代替-4
就能将减法统一到加法?不妨用二进制加加看:1010 + 1100 = 1 0110
,可见多了一个进位,对应表盘就是越过了一次刻度0,只要把进位丢掉就能得到正确的结果。从十进制角度来看,丢掉进位的行为可以看成是将加法的结果对2n取模,对于本例就是(10 + 12) mod 24 = 6。对于加法器来说,忽略进位是非常简单的。看来我们的目标要实现了!
在宣布结果之前,不妨再来看看两个负数相加是不是也符合我们的期待:0 + (-1) + (-1) = (-1) + (-1) = -2
:
至此,不难总结出,当我们使用n位编码时,让正数和0的编码保持其原码,让负数的编码为该负数加上2n得到的正数的原码,这种编码规则就可以将加法和减法统一起来,进一步的,考虑到除法可以用减法来做,乘法可以用加法来做,可以将四则运算都统一到加法,这对硬件(运算电路)设计来说是非常有意义的。
为了不用对正负数分情况讨论,我们需要一个补码的统一(对正负数皆适用)的定义,这个定义可以这么表达:用n位二进制数以补码来编码整数,可以编码的整数范围为[−2n−1, 2n−1),对于X∈[−2n−1, 2n−1),其补码[X]补=(X+2n) mod 2n=X mod 2n的原码。
当我们要计算某个整数的补码时,直接从定义式出发比较麻烦,我们可以根据定义式得出一些规律。显然,正数和0的补码就是其原码。对于负数,设-1
对应的正数为1
,-2
对应的正数为2
,依此类推,则负数的补码为2n减去其对应的正数后,得到的那个正数的原码。以n为32为例,2n=0xffff ffff + 1,而0xffff ffff与一个正数相减,其实就是将该正数的每个二进制位取反,因此负数的补码是其对应正数的原码按位取反后再加1。
相反,将补码转换为十进制整数时,我们可以按位权来算:
当然,也可以根据上文总结出的转换规律的逆过程来算:符号位为0,则直接当原码转换;符号位为1,则减1,再按位取反,之后再当原码转换。
2 整数在计算机中的表示
考虑到补码可以将减法统一到加法,有利于运算电路的实现,因此从上个世纪50年代开始,整数都采用补码的形式在计算机中存储及运算。