最初的方案,MSB是符号位,其它位是数据位。例如:0b1001表示-1, 0b0001表示1。此种编码方案,被称为sign and magnitude符号和量值法(中文专业词汇:原码) 。 此方案有明显的缺点。首先要设计特殊电路关照符号位,其次是要设计“减法器”电路,增加了硬件成本。替代方案就是two's complement二的补数方案(中文专业词汇:补码)。还有一个one's complement一的补数方案(中文专业词汇:反码),也被废弃不用( 因为我们不是搞计算机考古专业的,不需要考究编码方案的变更历史轨迹)。考虑一个4bit的计数系统,可以表示的范围是:[0,15], 使用[1,7]区间为正数编码;为使用[8,15]这个区间为负数编码。1000映射成-8,完全是人为的合理约定。这样所有的负数都是首位是1。
2进制 | 十进制 | 表示的编码 |
---|---|---|
0000 | 0 | 0 |
0001 | 1 | 1 |
0010 | 2 | 2 |
0011 | 3 | 3 |
0100 | 4 | 4 |
0101 | 5 | 5 |
0110 | 6 | 6 |
0111 | 7 | 7 |
1000 | 8 | -8 |
1001 | 9 | -7 |
1010 | 10 | -6 |
1011 | 11 | -5 |
1100 | 12 | -4 |
1101 | 13 | -3 |
1110 | 14 | -2 |
1111 | 15 | -1 |
现在我们验证一下这套编码方案:
例1:3 - 2 = 3 + 14 = 17(吹掉16)= 1
关键在于如何吹掉那个16?
0011 - 0010 = 0011 + 1110 = 10001 = 1(假设是4bit的存储器,则最高位没有物理容器承载,当然被丢到)
例2:3 - (-2) = 3 - 14 = 3 + 2 = 5
对于一个N位的存储系统,数字N的补数计算公式为:
N` = 2^n - N。例如:2的补码 = 16 - 2 = 14
这个公式有乘方,还有减法。设计补码的初衷就是为了规避减法电路,这么就会陷入逻辑陷阱死循环。
有聪明的计算机科学家发明了一下聪明算法:
2^4 = (8+4+2+1) + 1 (注:二进制10000 = 1111 + 1)
∴ 2^4 - 1 = 8+4+2+1
∴ 2^4 - 1 = 1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 (1)式
假设一个数N,用二进制表示为ABCD,则N用十进制表示为:
∴ N = A*2^3 + B*2^2 + C*2^1 + D*2^0 (2)式
(1)式 - (2)式
2^4 - 1 - N = 1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 - (A*2^3 + B*2^2 + C*2^1 + D*2^0)
∴ 2^4 - 1 - N = (1 - A)*2^3 + (1 - B)*2^2 + (1 - C)*2^1 + (1 - D)*2^0 把1移到右边
∴ N` = 2^4 - N = (1 - A)*2^3 + (1 - B)*2^2 + (1 - C)*2^1 + (1 - D)*2^0 + 1 (4)式
(4)式告诉了我们如何求一个数N的补数N`的开挂算法:由于A,B,C,D无非是0或1,则1-X等于求X的bit反,就是1的反是0,0的反是1。
设A,B,C,D四个位的反依次是A`,B`,C`,D` 则:
N` = (A`B`C`D`) + 1 (5)式
正数的补码是:它自身。例如: +3 ==> 0011
负数的补码是:绝对值对应的那个正数各位取反加一。例如: -3 ==> 0011 ==> 1101
这个规则应用于编程语言的文字量,或者从标准输入到系统的一个"-3"这样的文本,把这些东西放入物理存储器。
我们需要明白,求补码和求补数的区别联系。这样当我们需要知道1101这个编码对应是多少时,才有了思路。
1101假设是一个数的补码编码,则把它看成一个正整数为13,其对应的补数为3,则1101代表了-3,这样才能输出['-,','3','\0']