这次来详细说说补码。
计算机是如何处理减法的?实际上用加法器就可以实现,不过要对编码做一些调整。下面我来以2bit的简单例子来分析。
通常,我们对二进制数的编码都是直接映射到它相应的十进制数。
| 二进制数 | 十进制数 |
|---|---|
| 00 | 0 |
| 01 | 1 |
| 10 | 2 |
| 11 | 3 |
那么在一个二位加法器中计算加法实际上发生了什么事呢?如果计算1+3, 对应的二进制计算为01+11 = 1 00, 而1则作为了进位,实际输出是00.不难看出,如果假定我们的加法器只有两个2bit输入,一个2bit输出,那么输出的结果一定是输入的和对4的余数。3+3 = 6 = 2(mod 4) 也即 11 + 11 = 10(忽略了进位)。
现在我们希望加法器也能够处理减法。我们知道减法可以看作加法,2 - 1 = 2 + (-1), 这提醒我们可以对编码做一些调整,在编码中就出现负数。
那么应该如何编码呢?我们希望,编码之后,编码的计算能与二进制数的计算对应, 这要求编码的计算结果仍是对4的余数。
用a, b, c 表示二进制数(也表示与其自然对应的十进制数), 用f(a), f(b)表示编码的结果,现在我们希望如果, 就必然有, 其中表示算式是对4取余的结果。
很自然地,一个简单地想法就是, 因为, 这样编码中就出现了负数。当然,如果这样编码的话,就全是负数了。
| 二进制数 | 对应编码 |
|---|---|
| 00 | -4 |
| 01 | -3 |
| 10 | -2 |
| 11 | -1 |
如果检验的话,对10 + 11 = 01, 仍有(-2) + (-1) = (-3)。不过这显然不是我们期望的,我们还希望编码中有正数的存在。4个编码的话,可以分成2个非负数,2个负数。这样对4余数的集合就是{-2, -1, 0, 1}。对应的编码即,不过是在我们约定下的对4余数的集合中选取。
| 二进制数 | 对应编码 |
|---|---|
| 00 | 0 |
| 01 | 1 |
| 10 | -2 |
| 11 | -1 |
当然对4余数的集合也可以选成{-1, 0, 1, 2},不过这样一来就不像上述编码能从二进制数的首位就看出这个数是否为负。
在实际中,情况是怎样的呢?比如要计算2-1, 发来的二进制数实际上是自然的二进制数码:01(1), 10(2), 显然我们要把1转换成-1,才能在加法器中应用。也就是说想算减x的话,就要由x推出(-x)的编码。也就是要用原码(二进制数与十进制数的自然对应, 正数的补码等同于原码)推出补码。
用数学语言描述,x表示原码(0,1,2,3),y = f(x)表示补码(0, -1, -2, 3), g(y)是f的反映射,x = g(y)(0, 1, 2, 3)。现在要根据x表达g(-x), 由于, 也即(两边同时取反映射), 所以, 所以 ,而对应编码的和为0(比如说01 + 11 = 0)可以看成一个是另一个的取反加1。举个例子,2的补码(或原码)是10,-2的补码则是01(取反)+ 1 = 10。(这一点的证明也不难,只要注意到这是余数加法,而对x取反即。)
以上的结论即知道x的补码,求-x的补码即为x取反加1。简记为。
将4对应到即可扩展到更多的bit数。
在上一节的ALU里,我们是怎么计算减法的呢?输入两个数x, y,要根据控制指令得到x-y。 实际上只要先对x取反,让取反的结果与y相加,再对结果取反即可。
证明如下: