2.2.6【2020统考真题】


好的,这是一道深入探讨计算机乘法实现的经典大题。它从软件模拟(无乘法指令)到硬件实现(组合逻辑与时序逻辑),再到溢出判断,全面覆盖了乘法运算的各个层面。
我们来详细地解析这道题。
首先,附上题目原文:
题目原文
(5)【2020统考真题】有实现 x × y 的两个 C 语言函数如下:
unsigned umul(unsigned x, unsigned y) {return x*y;}
int imul(int x, int y) {return x*y;}
假定某计算机 M 中的 ALU 只能进行加减运算和逻辑运算。请回答下列问题。
- 若 M 的指令系统中没有乘法指令, 但有加法、减法和位移等指令, 则在 M 上也能实现上述两个函数中的乘法运算,为什么?
- 若 M 的指令系统中有乘法指令, 则基于 ALU、位移器、寄存器及相应控制逻辑实现乘法指令时, 控制逻辑的作用是什么?
- 针对以下三种情况:a) 没有乘法指令;b) 有使用 ALU 和位移器实现的乘法指令;c) 有使用阵列乘法器实现的乘法指令,函数 umul() 在哪种情况下执行的时间最长? 在哪种情况下执行的时间最短? 说明理由。
- n 位整数乘法指令可保存 2n 位乘积, 当只取低 n 位作为乘积时, 其结果可能会发生溢出。当 n = 32, x = 2³¹- 1,y = 2 时, 有符号整数乘法指令和无符号整数乘法指令得到的 x × y 的 2n 位乘积分别是什么 (用十六进制表示)? 此时函数 umul() 和 imul() 的返回结果是否溢出? 对于无符号整数乘法运算,当仅取乘积的低 n 位作为乘法结果时, 如何用 2n 位乘积进行溢出判断?
综合解析与分步详解
问题1分析:用加法和位移模拟乘法
- 结论: 能实现。
- 为什么:
- 乘法的本质是重复的加法。例如
5 * 3就是5 + 5 + 5。 - 在二进制世界中,这个过程可以被加法和位移操作极大地优化。我们以无符号乘法
x * y为例,可以模仿手算二进制乘法的过程:- 检查乘数
y的最低位。 - 如果最低位是1,则将当前结果加上被乘数
x。 - 将被乘数
x左移一位 (相当于x = x * 2)。 - 将乘数
y右移一位 (相当于y = y / 2)。 - 重复以上步骤,直到
y变为0。
- 检查乘数
- 这个过程只涉及判断(逻辑运算)、加法和位移,而这些指令在M中都存在。因此,编译器可以将
x*y这样一个高级语言操作,翻译成一段由这些基本指令组成的循环程序(微程序),从而在软件层面实现乘法。有符号乘法(如Booth算法)也可以用类似的基本操作组合实现。
- 乘法的本质是重复的加法。例如
问题2分析:乘法指令中控制逻辑的作用
- 背景: 当硬件直接支持乘法指令时,意味着CPU内部有一个专门的乘法器部件。这个部件通常由ALU、位移器、寄存器(如MQ乘商寄存器、ACC累加器)等数据通路元件构成。
- 控制逻辑的作用: 控制逻辑是这个乘法器部件的“大脑”或“指挥官”。它的作用是发出控制信号,按正确的时序协调数据通路中各个部件的工作。
- 具体作用:
- 控制循环次数: 根据操作数的位数(例如32位乘法需要循环32次),生成节拍脉冲,控制操作的步数。
- 控制操作类型: 在每一个节拍(时钟周期)内,根据乘数相应位是0还是1,决定ALU是执行加法操作还是不操作(直接通过)。
- 控制数据流动: 控制位移器在每个节拍结束时执行位移操作,并控制数据在寄存器之间的传送。
问题3分析:不同实现方式的性能比较
- 函数
umul(): 这是一个无符号乘法。 - 情况 a) 没有乘法指令:
- 乘法是通过一段软件程序(由多条指令组成的循环)来模拟的。
- 执行过程需要反复地取指令、译码、执行多条指令(如判断、加法、位移、循环跳转等)。这个过程涉及到多次访存和CPU内部的完整指令周期,开销非常大。
- 情况 b) 使用ALU和位移器实现的乘法指令:
- 这是用时序逻辑实现的硬件乘法器。乘法操作由一条单一的机器指令完成。
- CPU执行这条指令时,控制逻辑会产生一系列微操作,在多个时钟周期内完成计算(例如,32位乘法可能需要32个或更多时钟周期)。
- 相比(a),它不需要反复取指译码,效率大大提高,但仍然需要多个时钟周期。
- 情况 c) 使用阵列乘法器实现的乘法指令:
- 阵列乘法器是用组合逻辑实现的硬件乘法器。它由大量的加法器和逻辑门直接构成一个运算“电路阵”,可以看作是把循环过程在空间上展开了。
- 特点: 只要输入信号稳定,结果几乎是“瞬间”产生的(只受门延迟影响),整个乘法运算可以在一个时钟周期内完成。
- 结论:
- 执行时间最长: 情况 a)。因为软件模拟需要执行多条指令,开销最大。
- 执行时间最短: 情况 c)。因为组合逻辑的阵列乘法器速度最快,通常一个时钟周期即可完成。
问题4分析:乘法溢出
1) 计算2n位乘积
-
n = 32, x = 2³¹ - 1, y = 2
-
x的32位二进制表示:
0111 1111 1111 1111 1111 1111 1111 1111- 十六进制:
7FFFFFFF H
- 十六进制:
-
y的32位二进制表示:
0000 0000 ... 0000 0010- 十六进制:
00000002 H
- 十六进制:
-
计算 x × y:
x * y = (2³¹ - 1) * 2 = 2³² - 2
-
2³²的64位二进制表示:
1后面跟32个0,再跟32个0。即1 0000...0000(共64个0)。 -
2³² - 2 的64位二进制表示:
1 00000000 ... 00000000 - 00000010 ---------------------------0 11111111 ... 11111110这个结果是一个64位的数,其最高位是
0,后面31个1,然后是...1110。 -
转换为64位十六进制:
- 高32位:
0111 1111 ... 1111 1111->FFFFFFFF H - 低32位:
1111 1111 ... 1111 1110->FFFFFFFE H
- 高32位:
-
结论:
- 无论是有符号乘法还是无符号乘法,当操作数
x和y都是正数时,其完整的2n位乘积的二进制位是完全相同的。 - 因此,
x × y的64位乘积是FFFFFFFFFFFFFFFE H。
- 无论是有符号乘法还是无符号乘法,当操作数
2) umul() 和 imul() 返回结果是否溢出
-
umul()返回unsigned int(32位无符号)。- 范围:
[0, 2³² - 1] - 结果:
2³² - 2。这个值在无符号32位的表示范围内。 - 结论:
umul()不溢出。
- 范围:
-
imul()返回int(32位有符号)。- 范围:
[-2³¹, 2³¹ - 1] - 结果:
2³² - 2。这个值远远大于有符号32位的最大值2³¹ - 1。 - 实际截断结果: 完整的64位乘积是
...FFFFFFFFFFFE。截取低32位是FFFFFFFE H。这个补码表示的十进制数是-2。(2³¹ - 1) * 2的结果变成了-2,显然是错误的。 - 结论:
imul()发生溢出。
- 范围:
3) 无符号整数乘法溢出判断
- 背景: n位无符号数
a乘以 n位无符号数b,结果是一个2n位的乘积。我们只保留低n位作为结果。 - 溢出条件: 如果这个2n位的完整乘积,其值大于n位无日志数能表示的最大值 (
2ⁿ - 1),就意味着高n位不全为0,发生了溢出。 - 判断方法: 检查2n位乘积的高n位。
- 如果高n位全为0,说明乘积的值小于
2ⁿ,完整结果可以被低n位无损表示,没有溢出。 - 如果高n位不全为0(即至少有一位是1),说明乘积的值大于等于
2ⁿ,超出了n位所能表示的范围,发生了溢出。
- 如果高n位全为0,说明乘积的值小于
- 结论: 对于无符号整数乘法,当仅取乘积的低n位作为结果时,只需判断2n位乘积的高n位是否全为0。若不全为0,则发生溢出。
