401-乘法的运算过程
# 401-乘法的运算过程
乘法是我们日常生活中经常使用的运算, 如果是两个非常简单的数,我们用口算就能解决,即使是比较大的数我们也只要用一支笔和一张纸,就能够非常轻松的完成。这种方法我们在小学的时候就已经掌握了。
那么计算机又是如何实现乘法的呢?这个问题就比较复杂了,不过今天我将用这样最基本的工具为大家来揭示计算机实现乘法的秘密。
好了, 那么我们就回到小学的时代来看一看如何用笔在纸上进行乘法的运算。 我们要计算的这两个数是 2345 乘以 9876。 首先我们要做的是最低位的乘法, 5 乘以 6,那么 5 6 30,这还很简单, 但其实这里隐含了一个复杂的操作,那就是去查九九乘法表。 无论这个乘法表是牢记在了你的脑子里,还是藏在了你的口袋里, 那么总之我们要进行查表,发现 5 和 6,对应的是 30, 所以把这个 30 拿过来,放在这里,我们写上 0,标上进位 3。 那么接着做下一步,下一步是 4 6 24, 我们就要加上这个进位的 3 得到 27,在这里写上 7 并标上进位的 2。 然后是 3 6 18,标上了 0,写上了进位 2。 然后是 2 6 12 加上进位 2,,是 14,我们写上了 4,然后进位是 1。 当然又没有更高位了,所以直接把这个 1 也写上, 现在我们就得到了一个结果 14070, 然后很可惜这只是一个中间的结果, 那么即使是这个中间的结果,我们也经过了四次查表运算,还进行了四次的加法, 中间总共产生了四次的进位输出, 这么繁琐的一个过程,而且同样中间结果我们一共有四个。 最后我们还要把这样的中间结果再加起来 才能得到我们最后的成绩,这样看来乘法的运算,实际上是非常繁琐的。
那么我们能不能不要用这样繁琐的过程呢? 可不可以有一些简单的方法, 能够更好的解决这样的问题。
想要找到简单的方法,我们不妨先来看一看简单的情况, 我们找两个简单的数字,1000 乘以 1001, 同时我们也把刚才用过的那张纸找回来,放在一边作为一个参照, 那么对于我们新选的这两个数字,我们同样也进行乘法的运算,方法是一样的, 先拿乘数的最低位和被乘数的每一位进行相乘, 那么 0 乘 1 还是 0,1 乘以 1 就是 1, 进行这四次相乘以后得到了四个数,组成了我们第一个中间结果。 然后是乘数的第二位,也和被乘数的每一位相乘, 从而得到了第二个中间结果,然后是乘数的第三位,得到第三个中间结果, 最后是乘数的第四位和被乘数的每一位相乘之后得到最后一个中间结果, 我们再把这些中间结果加起来就得到了我们最后运算的乘积, 也就是 1001000,我们用普通的乘法运算的方法自然就可以完成这次乘法的运算, 但是对于这个例子的特殊情况我们发现实际上它展示了一种更为简洁的过程。 在介绍这个过程之前我们先把几个名词确定下来。 这个称为被乘数,这个是乘数,最后的运算结果是乘积,
那么对于这个例子,每个中间结果是怎么生成的呢? 原则其实可以很简单, 我们不用再去关心九九乘法表是怎么样的,也不用再去关心每一位产生的进位是不是要加起来, 我们只需要观察当前进行运算的乘数的位, 如果这一位是一,那我们就直接将被乘数放置在与它对齐的位置上就可以了。
如果当前参与运算的乘数位为零, 那我们则直接将零放置在与它对齐的位置上, 这就是我们经过简化后的运算方法, 针对这样的特殊情况每一个中间结果的产生都不需要去查找乘法表,都不需要进行加法的运算, 要么是被乘数本身,要么是全零,根据当前参与运算的乘数位 做一个非常简单的二选一的操作就可以了。 那么对于十进制来说这只是一种非常特殊的情况,并没有普遍的意义, 但是如果我们的运算当中只会出现一或零,那这就是一个通用的方法了。 那很好,二进制正好符合这个特点,所以我们现在显示的这个例子, 如果它表示的是一个二进制数的相乘,我们就可以使用这个非常简洁的运算方法了, 而这也正是计算机最终选择了二进制的一个重要的原因。
其实最开始的电子计算机也是使用十进制的, 我们不妨回到二十世纪四十年代去看两台我们非常熟悉的机器。 左边这一台就是 ENIAC 它采用的就是十进制, 而右边这一台是 EDVAC,它采用的是二进制。那么 ENIAC 采用的是十进制导致它内部设计的电路非常的复杂, 而 EDVAC 采用的二进制之后就大大简化了控制逻辑。 关于这一点,冯诺依曼在他的报告中有详细的描述。
冯诺依曼在关于 EDVAC 的报告草案中 对于计算机应该采用什么样的进制,进行了详细的分析。 他主要说了这么几点, 一,组成计算机的电子管是一种全或无的设备,它适合表示只有两个数值的系统。 那么二进制就是一个合适的选择,这也是我们通常说计算机为什么要采用二进制的原因。 但是仅仅有这一点是不够的,还有一个重要的原因,就是要有适合的运算方法, 在这份报告中冯诺依曼还提到,二进制可以大幅地简化乘法和除法的运算过程,尤其是对于乘法,如果采用二进制, 就不再需要使用十进制的乘法表,也不再需要产生每一个中间结果时所使用的加法了。 另外冯诺依曼还强调了一点,那就是十进制才是真正适合人类来使用的, 无论计算机当中采用什么样的进制,最终和人进行交互时,都应该使用十进制。 因此输入输出设备需要承担二进制和十进制之间的转换工作, 由此看来,如果想让计算机使用二进制而不是十进制,就必须要保证 二进制在这个数值的表示和运算方面都需要有很好的性能的提升, 要远远的超过额外的进制转换带来的性能损失这样才是可以接受的。
那好,我们再回来看这个二进制乘法的运算过程。 现在我们有了非常简洁的运算方法, 但是这个运算方法是否是和硬件的电路来实现,我们还需要来进一步的分析。 计算机的硬件资源和我们在纸上运算时可以比较随意地书写是不一样的, 那么还是用在纸上进行计算作为例子, 与刚才不同的是我们现在没有这么大一张纸了,我们把其中一部分剪掉, 只留下了能写被乘数,乘数和乘积的这三行的位置, 不过还有另一个变化就是我们不但有笔,而且还有橡皮, 我们之前在纸上写的数可以用橡皮擦掉。 那么就来看看在这么小的纸面上我们是否还能够完成刚才的乘法运算。
因为我们已经没有地方可以去记录中间的结果了, 而在运算的最开始用来记录乘积的这个地方是空的, 所以我们要想一想如何充分的利用这个区域, 在运算开始的时候我们可以把乘积记为 0, 然后我们开始运算。 第一步,对应的乘数位是 1,所以我们现在 已经知道这个中间结果就直接是被乘数, 但是我们现在已经没有地方可以记录这个中间结果了,所以每产生一个中间结果我们就直接把它加到乘积上。 于是我们就用橡皮把现在的乘积的数给擦掉,然后写上新的乘积, 其实也就是一个临时的中间结果。 好我们这样就完成了第一步。
在产生完这个中间结果之后,我们注意下一个中间结果实际上 是往左侧一位的。为了不至于过一会儿忘记了中间结果与乘积之间的对齐关系, 我们现在就重新写一遍被乘数,把它放在下一个中间结果应该出现的对齐的位置。 好了,然后我们再开始执行第二步的运算, 这步中间结果是 0,我们可以认为把这个 0 加到了现在的临时结果上, 也可以认为不做任何操作,然后呢我们会再把被乘数向左移动一位, 再检查最后的乘数位,还是 0
然后我们再把被乘数向左移动一位,再检查乘数的对应位,这是 1,所以这一次的中间结果就是被乘数 1000。 我们到现在的被乘数和乘积之间的对齐关系,把这个中间结果加到当前的乘积上。 我们注意 现在我们已经检查完了乘数的每一位,所以这个运算已经完成了, 现在在这张纸的最下面这一行就是我们得到的最终的运算结果。 这样我们就可以在没有空间记录中间结果的情况下完成了这个乘法的运算。 这就是一个适合硬件的乘法运算的过程。
现在我们已经将乘法运算改造成了适合硬件实现的方式, 那么下一步我们就要开始制造我们自己的乘法器了。 我们希望之后可以不再用纸和笔进行乘法的运算, 而用我们自己制造的乘法器自动地完成我们想要的运算的乘法。