整数的加减乘除计算的实现。
# 整数运算
# 算术逻辑单元 ALU
寄存器存储数据、结果、flags
控制器控制 ALU 操作和数据传入的信号
# 全加器
关注与或异或的符号
计算结果和进位
F 可以理解为,是奇数个 1 的时候,结果为 1;是偶数个 1 的时候,结果为 0。
进位则是,有两个 1 的时候即可进位。
异或门只能有两个输入端,异或门的时延不一定为 3ty。
在网上找了一些实现异或的方法,可以看出,时延可以是 2ty 也可以是 3ty,应该是由异或门实现的方式决定的。
非门算不算时延? 非门算时延
减少了一个与门,但是时延仍然是 6ty
这里可以解释为,因为 X 和 Y 的或和异或仅在 X、Y 均为 1 时不同,但 X、Y 均为 1 时,前面的与为 1,并不影响结果,所以两个式子相等。
# 串行进位(行波进位)加法器
公式来源于前面的全加法器,理解一样。
需要等待 “进位输出信号”,所以缺点是延迟慢。
# 全先行进位加法器
不需要等待 n-1 位算完,就可以开始计算第 n 位,把能计算的先提前计算好。
1ty:Gi 和 Pi 的计算,Gi 和 Pi 的计算可以同时进行,只需要 1ty
2ty:计算 C 的值,因为需要先进行与操作,再进行或操作,所以需要 2ty,因为 Ci 只和 P\G\C0 的值有关,所以不需要等前一位的进位算好。
3ty:Xi 和 Yi 的异或可在前三个时延做好,只需要再和 Ci-1 做异或即可算出 F。
# 部分先行进位加法器
加法器延迟过于严重,但是全先行进位又难以实现,所以采用了两者的混合杂交。
3ty:第一个全先行加法器需要算出 G、P,耗时 1ty;然后需要算出所有的进位,耗时 2ty;与此同时,其他三个 CLA 也算好了自己的 G 和 P。
2ty:第二个 CLA 收到 Cin 之后,需要 2ty 计算 Cout。
2ty:同上,为第三个 CLA 的计算。
5ty:2ty+3ty,第四个 CLA 计算 C 需要 2ty,然后再和 X、Y 进行异或操作,需要 3ty。在此之前,其余的 CLA 已经完成了异或计算。
# 加法
溢出的情况只能是两个正的变成负的,或者两个负的变成正的。
Xn | Yn | C | Fn | overflow |
---|---|---|---|---|
0 | 0 | 0 | 0 | 不溢出 |
0 | 0 | 1 | 1 | 溢出 |
0 | 1 | 0 | 1 | 不溢出 |
0 | 1 | 1 | 0 | 不溢出 |
1 | 0 | 0 | 0 | 不溢出 |
1 | 0 | 1 | 0 | 不溢出 |
1 | 1 | 0 | 0 | 溢出 |
1 | 1 | 1 | 1 | 不溢出 |
判断是否溢出的 flag 就是在 Cn-1 和 Cn 上加一个异或器
Xn | Yn | Cn-1 | Cn | overflow |
---|---|---|---|---|
0 | 0 | 0 | 0 | 不溢出 |
0 | 0 | 1 | 0 | 溢出 |
0 | 1 | 0 | 0 | 不溢出 |
0 | 1 | 1 | 1 | 不溢出 |
1 | 0 | 0 | 0 | 不溢出 |
1 | 0 | 1 | 1 | 不溢出 |
1 | 1 | 0 | 1 | 溢出 |
1 | 1 | 1 | 1 | 不溢出 |
# 减法
减法就是在加法器上加一个选择器,sub 给一个信号
# 乘法
实际上,加法和移位可以合并,因为始终上升沿到来之前,各寄存器中的数据会被取出,因此不需要 2-3 个时钟周期,1 个时钟周期即可完成。
由于乘法会出现溢出的问题,所以出现了原码一位乘法和布斯算法。
# 原位一位乘法
# 补码一位乘法:布斯算法
# 基本思路和证明
每次根据两位决定直接移位 / 加 / 减(共四种情况)
- 00、11—— 直接移位
- 10 —— 减
- 01 —— 加
# 黑书证明
用 **2 n + 2 n-1 + ... + 2 n-k = 2 n+1 - 2 n-k** 可以表示正乘数和负乘数
# PPT 证明
为什么可以这样用布斯算法计算?
# 易错点:算数移位
错误原因:补位负数补 1,不论是何种情况,右移都使得 A 的最左位,即 An-1 不仅移入 An-2 中,而且还保留在 An-1 中。这是为了保持 A 和 Q 中数的符号,因为它保留了符号位,所以被称为算数移位。
实际上,根据黑书,应该还有一位 Q-1 寄存器,放在 Q 最低有效位的右边,负责保存丢失的那位。
# 乘法溢出
带符号整数:因为乘法的补位是根据当前最高位计算的,如果有变化,则说明有溢出
无符号整数:无符号整数应该没有 1 的出现
# 除法
不同的高级算法对于 1/0 0/0 的计算结果处理是不同的
如在 js 中,1/0 为正无穷;c 中会报错
在十进制除法中,需要用到乘法;在二进制中则不需要。
除数和被除数符号不一样时,不同的编程语言也会得到不一样的答案。
注意补齐被除数的符号位,从次高位开始计算。
# 除法器
对应于手工进行除法运算,
因为需要补全被除数的位数,所以需要 2n 寄存器;
因为除数相对于被除数的次高位开始移动,所以需要 2n 位支持右移的寄存器;
除法流程图 —— “是否为第 N+1 次循环”
和乘法不同 ——N 次循环
如何做优化?
常见的思路 —— 并行操作 / 硬件节省
不能够做并行操作
可以做一些硬件的优化
# 优化的除法器
# 异号的除法如何处理?
结果都是和余数进行比较,操作之后,如果相对于余数的符号改变,则不够;如果相对于余数的符号不变,则够。
例子:
可以对照上下(36、37)两张图理解异号的除法,
被除数为 11111001,它的值为 - 7
- 1111 + 0011 为 10010 符号改变
- 1110 + 0011 为 10001 符号改变
- 1100 + 0011 为 1111 符号不变,且绝对值减小,因此可以加,且商上 1
- 1111 + 0011 符号改变
参考黑书的话,
余数的符号 = 被除数
商的符号 = 被除数 * 除数
因此,此题中,商和余数都需要取负数,所以商要取补码
# 补码除法运算过程
# 不恢复余数除法
可以参考之前的恢复余数的操作理解
如何理解大致思路:
由下面的通式表示:
ri+1=2ri+(1-2Qi)Y
式中 Qi 为第 i 次所得的商,若部分余数为正,则 Qi=1,部分余数左移一位,下一次继续减除数;若部分余数为负,则 Qi=0,部分余数左移一位,下一次加除数。由于加减运算交替地进行,故称为原码加减交替法。
# 补码不恢复余数除法流程图
同号说明 “够大”,异号说明 “不够大”
注意不恢复余数补码除法分为两个流程,首先需要判断符号然后初始化。
计算流程理解:
流程一:因为余数和除数异号,所以采用加的方式。加完后发现同号(实际上是由异号变为同号),补 1,实际上是取反的操作。
流程二:根据 X 和 Y 是否同号,继续操作。
关于商的修正:
Z 左移一位,根据是否够加,上 1 或 0。
如果被除数和除数异号,说明商是负数。在求每一位的时候,已经取了反,因此再加 1 即可。这样的操作,比最后再计算商的总体的补码简单。
关于余数的修正:
余数和被除数的符号相同。
如果符号不同,则说明余数多加 / 多减了一个除数。如果被除数和除数的符号相同,说明余数和除数符号不相同,加上除数进行修正;如果被除数和除数符号不同,说明余数和除数符号相同,减去除数进行修正。
在最后多加了一个 Y
符号位,不恢复余数补码除法运算和除数比,恢复的和被除数相比
因为除法器效率不高,所以用右移运算实现简化
能整除的时候,被移除的全为 0;
不能整除的时候,移除的数存在非 0,
-14/4 负数补位 1
# 阵列除法器
实际生活中并不是斜着排列的
# 总结
全加器是两个半加器组合起来的,半加器不考虑进位。
全加器算了两次 X+Y:第一次 X+Y 得到 Z,第二次 Z+Cin 得到结果。
注意各整数运算的优化过程