5.5 除法的运算过程
计算机组成
5 乘法器和除法器
5.5 除法的运算过程
在加、减、乘、除这样的基本算数运算当中,除法是最为复杂的。因此,我们想要实现硬件的除法器,还是从最简单的情况开始说起。
我们还是采用纸笔进行模仿除法运算的方式,来回顾一下除法的运算过程。这里是两个十进制的数,被除数是1001010,除数是1000。这是两个经过精心挑选的数,用它们进行除法运算,运算的过程中只会出现0或者1,所以看上出又像是二进制表示的数。所以我们通过这个例子可以看出十进制的除法运算和二进制的除法运算之间的联系。这个运算的过程,我们已经非常熟悉了,所以我们快速地来看一遍。
首先将除数和被除数,从高位开始对齐,然后将对齐的这部分进行一个减法,当我们发现减完的结果是一个正数,也就是所谓的够减的时候,我们就在上面标一个1,然后把减完的差放在下面,再从被除数后面的位拿一个数下来,这时候我们用眼睛看一看就可以发现,现在这个数还不够去减这个除数。
所以我们在上面标一个0,然后再多拿一位,还不够减去除数。
那么我们在最上面再标一个0,然后再拿一位下来。现在我们发现,这时候已经够减这个除数了(1010 > 1000)。
%20
好,那我们在这上面标一个1,然后把除数写在这里,再执行一次减法,减完的结果已经比除数小了(10 < 1000),而且被除数那里也没有多余的位可以拿下来继续运算,这样我们这个除法就已经完成了。
先得到最上面的这个数,就是这个除法运算的商;而最下面的这个数,我们称为余数。如果用一个式子来表达这四个数的关系,那就是被除数等于商乘以除数再加上余数。当然我在描述这个除法运算的时候非常的口语化,而且也有人的智力因素参与其中。比如说用眼睛看一看够不够减之类的,这样的描述过程是很难用硬件去实现的。想要设计一个硬件的除法器,首先我们要从硬件实现的 角度出发,来考虑如何描述除法的运算过程。那我们用这样的方式再来看另一个例子。
这里就是两个二进制数的除法了。被除数是 00000111,这就是十进制的7;而除数是0010,这就是十进制的2。所以,这就是做7除以2。对于硬件实现,我们首先要考虑的是这个运算的操作数的宽度,这里我们可以看到,被除数是一个8位宽的数,而除数是一个4位宽的数。因此,在这样的情况下,即使高位是0,我们也不能将这个0省略,因为它们实实在在地在硬件中占据了位置。
很显然,我们可以把被除数放在一个8位宽的寄存器当中,同时我们还要注意,被除数是在不断的和除数进行减法的操作,在经过几轮之后,减法的运算结果最后就产生了余数。所以,如果我们将每次减法运算的结果都放回到被除数的寄存器当中,那么这个寄存器又可以被称为余数寄存器,现在我们就有了第一个部件,就是一个8位宽的余数寄存器;然后,我们还需要一个部件来保存除数;还需要一个部件来保存商。
那我们在纸上进行除法运算的时候。第1步,会把除数抄写到这里,并将被除数当中最高的4位和这个除数进行对比。经过比较,我们发现被除数的这一部分是比除数小的(0000 < 0010)。所以,不能执行这个减法。因此,我们在与当前除数最低位对齐的这个地方,标上我们得到的第一位的商,也就是0。目前执行的这个过程,我们就称为第一轮。然后我们会怎么做?
我们接下来要做的就是把除数往右挪一个位置,把除数再抄一遍。好,我们把除数抄在这里,如果我们忽略这些数字底下衬着的这些带颜色的框,那它实际上和在纸上进行除法运算我们所写的内容是一样的;如果我们带上了这个写了除数的长条形的方框,那我们又可以发现这个除数0010好像是在这个方框中右移了一位,这样我们就可以考虑在硬件上用一个带右移的寄存器来实现这个功能。那么这个时候又需要用被除数和除数进行一个减法运算了。
那现在够不够减呢?实际上还是不够的(0000 < 0010)。其实我们不用去找应该对齐哪几位进行减法的比较,如果我们直接把这个写着除数的长条方框看成一个8位的寄存器的话,那就可以把其中没有标明数字的地方都看做是0,每次都是把这个标着除数的8位的寄存器的内容和上面标着余数的8位寄存器的内容进行比较,那么现在这个除数还是更大一些(0001 0000 > 0000 0111)。所以,这一轮也不能执行减法。这样,我们在商这个位置再标一个0。我们要注意,在纸上运算时,我们直接就写上了第二个0,而现在我们在硬件上只给商预留了4个位置,也就是说是一个4位的寄存器。所以,这个操作就好比将商的这个寄存器往左进行了一个移位,并在最右端补入了一个0,那这就是第二轮。
然后我们继续,再将除数往右边挪一位和被除数进行比较,现在还是除数更大(0000 1000 > 0000 0111)。所以,这一次还是不能执行减法,而商再写上一个0,这就是第三轮。
我们再看下一轮。再向右移一位除数之后,我们发现,现在被除数更大了(0000 0111 > 0000 0100)。可以执行一次减法了,减完的结果就是0000 0011。因为执行了一次减法,所以我们在商对应的位置标上了1,也就相当于对这个商寄存器往左移动一位之后,在最右端补上了一个1。然后我们把这个减法的结果更新到余数寄存器当中去, 这就是第四轮。
但现在还没有结束,我们还需要把除数再往右移一位,并和当前的余数进行比较, 我们发现还可以进行一次减法运算(因为余数大于除数:0000 0011 > 0000 0010)。所以,在最上面,我们再把商从最右边移入一个1。然后,再回来看下边,我们再执行一次减法,得到了一个结果为0000 0001,并把这个结果再更新到余数寄存器当中去,这就是第五轮。现在我们就已经得到了最终的结果。最上面是商的最终结果:0011;最下面是余数的最终结果:0000 0001,也就是1。0011是十进制的3,所以7除以2,商为3,余数为1,这样的结果显然是正确的。
我们再来整理一下这个例子。
对于被除数,我们选择的是十进制的7;对于二进制,7可以用8个比特0000 0111来表示,而且被除数我们可以视为初始化时候的余数。
除数2,我们用4个比特的寄存器来保存,而且在运算的过程中,我们可以看作将除数不断地右移,并和被除数进行相减。
对于余数,虽然它最终的结果很小,但是因为它可以与被除数共享一个寄存器。所以,我们也用8个比特来表示。
对于商,在这个例子中是3。我们要注意到的是,在运算过程中,它的每一个比特是从高到低,也就是从左往右依次生成的,可以视为不断地左移而形成。在这里我们也用4个比特的寄存器来保存。
我们要注意,这个位宽和乘法运算正好是相反的。在乘法当中,我们一般会约定乘数和被乘数的宽度是一样的:如果两个都是4位宽的,则乘积最多为8位宽。所以,在除法当中我们也用了类似的约定:如果被除数是8位宽,则除数和商都约定为4位宽。
现在我们就有了一个适合硬件实现的除法运算过程了。
从这个事例我们就能够看出,除法运算确实是非常复杂。不过现在我们应该很有信心,只要我们能够用适合硬件的方式把这个运算过程描述出来,我们就应该能够把这个除法器设计出来。