乘法器

复习

  1. 第五章:由基础逻辑门(与或非三门)搭建了更复杂的逻辑门:异或门,同或门,多路选择器
  2. 第六章:设计完成了半加器,可以完成两个一位二进制数的加法,但不能处理来自低位的进位
  3. 第七章:设计完成了加法器,可以处理三个输入(两个加数和一个进位),并可以串联构成多位加法器
  4. 第八章:掌握了原码、反码和补码的概念,理解了补码可以统一加减法运算
  5. 第九章:设计完成了减法器,本质是加上一个负数(补码)

TL;DR

  • 乘法可以转换为多次加法
  • 通过移位和加法可以实现乘法
  • 乘法器的核心是加法器和移位器

正文

本质

  加减法有了,乘除法呢?加法有了,逆运算的减法也就有了,只要有了乘法,而除法是乘法的逆运算,问题应该也不大。

  回忆小学怎样算乘法。列竖式,相加。

  23
×  5
----
 115

  而乘法本质是反复加法。所以这实际上是:

  23 × 5 = 23 + 23 + 23 + 23 + 23

  十进制可以这样算,二进制呢?

逐位相乘

  不妨来试试: 3 × 5 = 15。简单起见,使用无符号数,没有符号位。

    0011   (3 的无符号二进制)
×   0101   (5 的无符号二进制)
--------    <-- # 表示占位符,没有意义
    0011    (被乘数 × 1)
   0000#    (被乘数 × 0,左移一位)
+ 0011##    (被乘数 × 1,左移两位)
 0000###    (被乘数 × 0,左移三位)
--------
    1111   (15 的无符号二进制)

  可见竖式也能得出正确结果:0011 × 0101 = 11112 进制无符号,也即 1510

  那就好办了,让一个二进制数,与另一个二进制数的 每一位 相乘,然后进行竖式一样的 移位 操作,最后加在一起就好了。因为补码可以参与运算,所以负数的乘法也解决了,下面就是实现了。

  相乘,仔细观察可以用 and(与门)解决,那—— 移位 呢?该怎么办?

移位

  我们没有介绍过移位,但应该能想到,上面逐位相乘的移位,最简单的方式,就是在最后一位后面补零,同时丢掉最高位(以上述竖式的中间结果为例):0011 不补零,0000 补一个,0011 补两个。

  而在对有符号数进行移位时,特别是右移,最高位需要补上符号位 ,不然负数就变成正数了。也就是说:对 1010 进行右移,结果应该为 1101。

思考题 1

  我们现在只有加法器,应该怎样移位和补位?以及, 设计一个最简单的乘法器,真的需要移位吗?

乘法器的构成

  根据上面的分析,乘法器需要以下部件:

  1. 与门阵列

    • 用于判断乘数的每一位是否为 1
    • 如果为 1,输出被乘数
    • 如果为 0,输出全 0
  2. 移位器

    • 对每一步的结果进行左移
    • 移位的位数取决于当前处理的是乘数的第几位
  3. 加法器

    • 用于累加每一步的结果
    • 可以复用之前设计的加法器

具体实现

  以 3(0011) × 5(0101) 为例,看看乘法器是如何工作的:

  1. 第一步(乘数最低位为 1,乘积结果为 0011,不移位,结果为本身):

    0011 × 1 = 0011
    
  2. 第二步(乘数次低位为 0,乘积结果为 0000,左移一位结果为 0000):

    0011 × 0 << 1 = 0000
    
  3. 第三步(乘数第三位为 1,乘积结果为 0011,左移两位结果为 1100):

    0011 × 1 << 2 = 1100
    
  4. 第四步(乘数最高位为 0,乘积结果为 0000,左移三位结果为 0000):

    0011 × 0 << 3 = 0000
    
  5. 最后,将所有结果相加:

      0011
    + 0000
    + 1100
    + 0000
    -------
      1111 (15)
    

优化思路

  上述逐位相乘有一个问题:

  如果是 M × 0100 这样就还好,只用加一个数字——因为只要乘数的某一位为 0,结果就必定为 0。所以逐位相乘,结果就是 0 + M + 0 + 0,实际上只用加 M 这一个数字。

  如果是 M × 1111 这样的呢?就需要加四个数字——M + M + M + M。这在乘数位数变多(比如 64 位)的时候,连续的 1 将极为致命,会严重拖慢计算速度,效率变低。

  目前学者想出了两种优化思路:

  1. Booth 算法

    • 利用连续的 1 可以转换为减法来减少计算步骤
    • 比如 "1111" 可以看作 "10000 - 1"
  2. 华莱士树

    • 并行处理多个部分积
    • 减少加法器的级联深度
    • 因为有些复杂,所以本指南略过

Booth 算法

  Booth 算法优化了连续的 1 这种情况。Booth 算法的核心思想是:对于 二进制补码 的每一位,我们可以通过判断它与它前一位的关系,来决定是否进行加减操作。

  看不懂?来个例子。

  设 X补码 = XsXnXn-1...X1X0, Y补码 = YsYnYn-1...Y1Y0,有 M × 00111000

  而 M × 00111000 = M × ( 23 + 24 + 25 ) = M × (01000000 - 00001000) = M × ( 26 - 23 ) = 26 × M - 23 × M。

  原乘数 00111000 中,10 表示连续 1 的开始,对应 - M × 2301 表示连续 1 的结束,对应 M × 26,那么可以得出移位的规则:

Yn+1Yn操作规则
00部分积右移一位
01【部分积 + X补码】,部分积右移一位
10【部分积 - X补码】,部分积右移一位
11部分积右移一位
  • 为什么要右移?
    • 往上划到逐位相乘的例子,计算部分积的时候,低位往往不受高位的数值影响(看上面的竖式更容易理解):
      • 计算完 0011,此时部分积为 0011,此后最右边的 1 不受后面计算的影响。
      • 计算完 0011 + 0000#,此时部分积为 0011,此后最右边的 11 不受后面计算的影响。
      • 计算完 0011 + 0000# + 0011##,此时部分积为 1111,此后最右边的 111 不受后面计算的影响。
      • 以此类推。
    • 因为不受高位的影响,所以可以右移将低位挤掉,使部分积与被乘数对齐,这样可以直接计算。
  • 【部分积 - X补码】怎样计算?
    • 回忆一下第四章:减法在二进制计算机里的定义是,加上 减数取反后的数值,再加一
  • 为什么移位规则里, 0110 的规则,一个正一个负?
    • 因为 10 表示连续 1 的开始,对应 - M × 2301 表示连续 1 的结束,对应 M × 26
    • 而上述 23 和 26,已经在移位中解决了,所以只用加减 X补码

思考题 2

  如果要计算带符号数的乘法,需要注意什么?

小结

知识点

  • 乘法的本质是多次加法
  • 二进制乘法的基本步骤
  • 乘法器的基本构成
    • 与门阵列
    • 移位器
    • 加法器
  • 乘法器的优化方法

参考资料

推荐

思考题答案(仅供参考)

思考题 1

  1. 移位可以使用 or 和 xor 门,最简单化的话,也可以将输入直接与输出错位相连。错位直接相连肯定比使用逻辑门效率更高,速度更快(图源:Wikipedia)。

  1. 其实最简单的乘法器不需要移位,只需要错位相加,当中错位可以让低位直接输出(图源:BiliBili 硬件茶谈)。

思考题 2

  带符号数乘法需要考虑:

  1. 结果的符号由两个操作数的符号决定(这里可以有一个偷懒的做法,见下面的 2, 3, 4)
  2. 先将操作数转换为绝对值进行乘法
  3. 根据符号规则确定最终结果的符号:同号得正,异号得负
  4. 如果结果为负,需要转换为补码表示

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪