《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

加、减法合一,明显更复杂了。 


实现原理:


减法和加法在某些方面是互为补充的,但两种计算的机制不同。加法从最右边一列向最。左边一列计算, 每一列的进位都加到下一列中去。减法不用进位,相反,要用到借位。

我们不会直接用这种方法,代替的是用一个小技巧,使不通过借位来实现减法。

首先,需要清楚地指明两个操作数,即减数(minuend)和被减数(subtrahend)。减数从被减数中去掉后,结果是二者之差(difference):

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

比如,计算253-176,很明显在计算最右一列的3-6就需要向5借一位,那么如何才能不需要借位呢?我们可以这样计算:

253-176=     253-176+999+1-1000=     999-176+253+1-1000

虽然用了两个减法和两个加法来代替一个减法,但是也因此省去了讨厌的借位。

把一个数从一串9中减去得到 的结果称为9的补数或补码。1 7 6的9的补数是8 2 3,反之,8 2 3的9的补数是1 7 6。这样做的好处 在于,无论减数是什么,计算9的补数永远不需要借位。

但是,如果减数比被减数大怎么办呢?例如计算:1 7 6 - 2 5 3。要省去借位来做这道题和前面的例子有所不同。首先你要求出 2 5 3的9的补数,即:

9 9 9 -2 5 3 =7 4 6

再把该补数和原来的被减数相加:

1 7 6+ 7 4 6=9 2 2

这时如果再对其加 1再减去1 0 0 0的话,就需要从 9 2 3中减去1 0 0 0,这又导致了借位。既然实际上前面已经加了9 9 9,所以在这里可以直接减去9 9 9:

9 2 2 -9 9 9

当做到这一步时,可看出结果是个负数,故需要交换两数位置,不过这样再做减法时已不需要借位。

当把253-176转化为二进制数时,问题变成:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

步骤1 用11111111减去减数:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

当计算十进制数减法时,减数是从一串 9中减去,得到称为 9的补数的结果。对于二进制 数减法,减数从一串1中减去,差称为1的补数。但请注意,求1的补数实际上并不需要做减法, 因为1的补数中,原来的 0变成1,原来的1变成0,所以,1的补数有时也称为相反数或反码。 (你是否还记得第11章中反向器的作用是把0变成1,把1变成0。)

步骤2 把步骤1中求得的补数和被减数相加:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

步骤3 对结果加1结果为:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

步骤4 减去1 0 0 0 0 0 0 0 0(2 5 6):

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

该结果就是十进制数7 7。 现在把两数颠倒位置后再做一遍:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

步骤1 从1111 1111中减去减数。得到补数:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

步骤2 把步骤1中的补数和被减数相加:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

现在,11111111必须再从结果中减掉。为了不借位,所以,我们先用 11111111减去步骤2 中的结果:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

这实际上是对步骤2中得到的结果取反。最后的结果是7 7,而真正的答案应该是-7 7。


加/减法器:


现在,已经可以改进加法机使它既能执行加法操作亦能执行减法操作。为使简便起见, 这个加/减法机只执行被减数大于减数的减法操作,即差为正数的操作。

       前面做的加法器:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

用于加/减法的新的控制面板有一点儿修改,它包含了一个用于选择做加法还是做 减法的额外开关。,当这个开关向下时表示选择加法运算,反之是选择减法运算。此外,只有最 右边的8个灯泡用于表示结果,第九个灯泡用来标识上溢 /下溢,它指明了一个不能用 8个灯泡 表示的数。当加法操作得到的和大于2 5 5(称为上溢,overflow)或减法计算中出现一个负数(下溢,underflow)时, 这个灯泡就会亮。减数比被减数大时,结果就是一个负数。

在法机主要增加了一个求 8位二进制数的补数的电路。由于一个数的补数就是取其每 一位的相反数,所以这个电路看起来很简单,就是 8个反向器而已。

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

但是如何让它只在做减法时才取反呢?。对它进行一下改进,如下图所示:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

没错,使用异或门来完成这一设想,回忆一下异或门的功能:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

可以看到,如果“取反”信号为 0,则异或门的 8个输出和 8个输入是相同的。若“取反”信号为 1,则输出取反。让我们把8个异或门集成到一个盒子里,称为求补器

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

将一个求补器、8位加法器及一个异或门按下图连接:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

注意上图中有3个信号都标识为“S U B”,这是加/减法转换开关。当该信号为 0时做加法, 为1时做减法。做减法时, B输入在送入加法器之前先求补。此外,做减法时,通过设置加法 器的进位输入端 ( C I )为1,使由加法器得到的结果加 1。对加法而言,求补电路没有起作用, C I输入也就是0。

“S U B”信号及加法器的C O输出作为异或门的输入来控制表示上溢 /下溢的小灯泡。如果 “S U B”信号为0(表示做加法),则当C O输出为1时灯泡点亮,这表示加法的和大于2 5 5。

当做减法时,如果被减数大于减数,则加法器的 C O端正常输出1,这表示在减法的最后 一步中要减去1 0 0 0 0 0 0 0 0。所以,只有当加法器的 C O输出为0时,上溢/下溢灯泡才被点亮。 这时减数大于被减数,差是个负数。上面这个加 /减法器现在还不能表示负数。


二进制负数的表示方法:


下面来看看帐户的例子,人们有时可以在帐户上看到负数。假设帐户上从来没有超过 $ 5 0 0的存款,而银行给我们的预支额是$ 500,这就意味着帐户上的数字在$ 4 9 9~-$ 500之间。 假设我们不会一次取出 $ 5 0 0,也不会写一张超过 $ 5 0 0的支票,同时我们只处理美元,而不考虑美分。

这些假设表明帐户能处理的数字范围是从- 5 0 0~4 9 9,总共1 0 0 0个数。所以我们可以只用三位十进制数且不用负号就可以表示所有范围内的数字。其中的关键在于我们不需要 5 0 0~ 9 9 9之间的正数,所以它们就可以用来表示负数。下面是其工作原理:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

所以我们可以用这样的表示法来表示从-500到499这样1000个数字:

500 501 502 ⋯ 996 997 998 999 000 001 002 003 004 ⋯ 497 498 499

这样形成了一个环形排序,最小的负数( 5 0 0)看上去是最大的正数(4 9 9)的延续。如果给 9 9 9加上1,通常得到1 0 0 0。但由于只处理3位数,所以实际上是0 0 0。 这种处理称为10 的补数(ten’s complement)。要把3位负数转换成1 0的补数,需从9 9 9中减去它再加1。换句话说,1 0的补数是9的补数再加1。例如,要把-2 5 5写成1 0的补数,应先从9 9 9中减去2 5 5得到 7 4 4,再加上1后得到7 4 5。

二进制中对应的系统称为2的补数。假设我们用 8位二进制数工作,范围从 0 0 0 0 0 0 0 0~ 11111111,对应于十进制的0~2 5 5。这时如果你想要表达负数,则以 1开头的每个8位数都表 示一个负数,如下所示:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

你可以表示的数的范围从- 1 2 8~1 2 7。最左边的一位称为符号位, 1表示负数,0表示正数。

要计算2的补数得先求出1的补数再加上1,这等同于先求反再加1。例如,十进制数1 2 5是 0 11111 0 1,要用2的补数来表示-1 2 5,可先取反得1 0 0 0 0 0 1 0,再加1就得到1 0 0 0 0 0 11。可用上 表来验证这个结果。要回到原来的数只需同样的操作:取反后加 1。

这个系统使不用负号就能表示正、负数,它也使我们只用加法规则就可以随意进行正、 负数运算。例如,计算-1 2 7 + 1 2 4,利用上表即得:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

和是十进制的-3。

这里要注意上溢或下溢,即结果大于 1 2 7或小于-1 2 8的情况。例如,1 2 5加1 2 5:

《Code:The Hidden Language of Computer Hardware and Software》读书笔记:第13章 如何实现减法

正确答案应该是250,而这里的结果是-6。一般而言,若两个操作数的符号相同,而结果的符号与操作数的符号不相同时,这样的加法是无效的(即加法运算产生了溢出!)。

现在,二进制数可以有两种不同的使用方法。二进制数可以是无符号的或有符号的,无符号的二进制8位数的表示范围从 0~2 5 5,有符号的二进制 8位数的表示范围从- 1 2 8~1 2 7。 这些数本身不会告诉你它们是否带有符号。例如,假设有人问:“1 0 11 0 11 0对应于十进制数的几?”这时,你必须先问清楚它是无符号数还是有符号数?它可能是 1 8 2或-7 4。