后面,我会用c语言只用位运算模拟一个加法,然后用这个加法实现“加减乘除”
这个要讲的其实不多,直接看例子吧:
【例1】x = +1001,y = +0101,求x+y
解:[x]补 = 0 1001,[y]补 = 0 0101
0 1001
+ 0 0101
-----------
0 1110
∴x + y = +1110
【例2】x = +1011,y = -0101,求x+y
解:[x]补 = 0 1011,[y]补 = 1 1011
0 1011
+ 1 1011
-----------
[1]0 0110
∴x + y = +0110
讲讲注意点吧:
还记得我说的那句话吗?减法的本质就是加法:[x-y] = [x] - [y] = [x] + [-y]
还是直接上例子吧:
【例1】x = -1110,y = +1101,求[x]补、[-x]补、[y]补、[-y]补
[x] = 1 0010
[-x] = 0 1110
[y] = 0 1101
[-y] = 1 0011
【例2】x = +1101,y = +0110,求x-y
解:[x]补 = 0 1101,[-y]补 = 1 1010
0 1101
+ 1 1010
-----------
[1]0 0111
∴x - y = +0111
运算中,两个数相加,如果大于字长,直接溢出,那不是贼伤,比如:
0 1011
+ 0 1001
-----------
1 0100
==============
1 0011
+ 1 0101
-----------
0 1000
正数+正数=负数?此为正溢。
负数+负数=正数?此为负溢。
那咋办嘛?好办,引入双符号位法(也就是变形补码):
上例子:
【例1】x = +1100,y = +1000,求x+y
解:[x]补 = 00 1100,[y]补 = 00 1000
00 1100
+ 00 1000
------------
01 0100
∴x + y 正溢出啦
【例2】x = -1100,y = -1000,求x-y
解:[x]补 = 11 0100,[y]补 = 11 1000
11 0100
+ 11 1000
------------
10 1100
∴x + y 负溢出啦
规律其实很简单,两个符号为相同就没毛病,不相同就出问题
诶,要是我不想双符号位,我就只想用单符号可以吗?当然可以!
来,再看一遍这个例子:
0 1011
+ 0 1001
-----------
1 0100
==============
1 0011
+ 1 0101
-----------
0 1000
正溢出:最高有效位有进位,符号位无进位
负溢出:最高有效位无进位,符号位有进位
牛逼吧?
我们硬核点,直接上【全加器】(简称FA,FullAdder)的真值表吧
A | B | Cin | S | Cout |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 1 |
1 | 0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 | 1 |
1 | 1 | 0 | 0 | 1 |
1 | 1 | 1 | 1 | 1 |
可以得到:
电路搭起来!(下图打印错误,左上角是或门,不是异或门)
完成了加法,那减法呢?很简单,把Cin置1,并且在B前面加个非门,把它反过来就行了
时间延迟的计算:
T为单级逻辑电路的单位门延迟,与门、或门1个T,异或门3个T,总延迟为6T
我们把FA抽象出来,封装成一个黑盒,用一些黑 魔法把它变成一个n位的行波进位加减器(也叫串行)
通过多加一个M控制器,走到B前面,加个异或门,也走到第一个Cin来完成控制加减法的操作
当前FA的Cin是上一个FA的Cout,得前一个算完,才能轮到我
时间延迟的计算:
t = 6 T + 2 T ∗ n + 3 T = ( 2 n + 9 ) T t = 6T + 2T*n + 3T = (2n + 9)T t=6T+2T∗n+3T=(2n+9)T
这个溢出的判断,就是单符号位法,只是不能判断正溢出还是负溢出
在模拟之前,我们先来看看大致过程,可能与上文不太相同:
4 + 5 = ?
解:[x]补 = 0 0100,[y]补 = 0 0101
异或:
0 0100
^ 0 0101
-----------
0 0001
# 异或后是无进位的结果
与:
0 0100
& 0 0101
-----------
0 0100
# 与后不为0,将结果左移1位,继续运算
=============================================
异或:
0 0001
^ 0 1000
-----------
0 1001
# 从这次开始的异或,都拿前一次异或的结果和前一次与完并移位的结果
与:
0 0001
& 0 1000
-----------
0 0000
# 与后为0,结束运算
=============================================
异或结果即结果:4 + 5 = 0100 + 0101 = 1001 = 9
代码怎么写呢?
#include
int add(int a,int b){
int xor_res,and_res,pre_xor;
xor_res = a ^ b;
printf("xor result is:0x%04X\n",xor_res);
and_res = a & b;
printf("and result is:0x%04X\n",and_res);
while(and_res != 0){
pre_xor = xor_res;
and_res = and_res << 1;
printf("and left shift 1 bit:0x%04X\n",and_res);
xor_res = pre_xor ^ and_res;
printf("xor result is:0x%04X\n",xor_res);
and_res = pre_xor & and_res;
printf("and result is:0x%04X\n",and_res);
}
printf("and result is 0,finished the operation!\n");
return xor_res;
}
int main(){
int a = 4;
int b = 5;
int result_add;
result_add = add(a,b);
printf("%d + %d = %d\n",a,b,result_add);
return 0;
}
一样用4-5的的例子:
4 + (-5) = ?
1 0101
解:[x]补 = 0 0100,[-y]补 = 1 1011
异或:
0 0100
^ 1 1011
-----------
1 1111
# 异或后是无进位的结果
与:
0 0100
& 1 1011
-----------
0 0000
# 与后为0,结束运算
=============================================
异或结果即结果:4 - 5 = 4 + (-5) = 0 0100 + 1 1011 = 1 1111 = -1
代码就在上面代码的基础上,稍稍封装一下:
int sub(int a,int b){
return add(a,-b);
}
我们跑一下:
xor result is:0x0001
and result is:0x0004
and left shift 1 bit:0x0008
xor result is:0x0009
and result is:0x0000
and result is 0,finished the operation!
4 + 5 = 9
----------------------------------------
xor result is:0xFFFFFFFF
and result is:0x0000
and result is 0,finished the operation!
4 - 5 = -1
硬核吧,哈哈哈哈哈哈哈哈哈
给你个式子,x=1101,y=1011,求其乘积,你怎么求?
1101(x)
* 1011(y)
----------------
1101
1101
0000
+ 1101
----------------
10001111
大概率是这么想的吧?人脑的思维模式就是这样算的,但不太适合机器:
那咋办呢?多亏于大规模集成电路的发展,出现了并行的流水式阵列乘法器
我们先来看看m*n位不带符号的阵列乘法器逻辑框图(需要m乘n个与门)
实现这个过程还是比较贴合人类的思维模式的:
可能还不够清晰,我们以5位乘5位的乘法器为例:
提到有符,就不得不想到补码,我们可以先看一下常用的补码电路:
符号与数值分开处理,符号采用异或电路,数值采用无符号阵列乘法器:
原码数据可以直接运算
若输入为补码数据,需转换成原码后再运算
E = 0,不变;E = 1,求补
看倒例题:设x=-15,y=-13,用补码求x*y
[x]补 = 1 0001,[y]补 = 1 0011
解:
符号部分:xn⊕yn = 1⊕1 = 0
数值部分:|x|=1111,|y|=1101(这部是把补码还原成原码)
1111(x)
* 1101(y)
----------------
1111
0000
1111
+ 1111
----------------
11000011
因为符号位为0,所以求补输出结果也为:0 11000011
规则:符号与数值分开计算
直接上例题吧:x=0.1001,y=0.1011.求x÷y
一样,这是符合我们人脑的思维模式;但机器需要先做减法,看余数正负才知道够不够减,减下去不够减,就得恢复余数,这就有了恢复余数法,当然,也有不恢复余数法(加减交替法),根据余数符号继续往下减
这可是组成我们阵列的基本单元,FA的升级版
P=0,CAS做加法
P=1,CAS做减法
考虑最大的信号时延,则对于Ci的输入,Ci+1的时延为:3T
一样,做除法器也是需要牛的阵列
2n位除以n位的不恢复余数阵列除法器,含有(n+1)^2 个CAS单元。
考虑最大信号延迟,除法执行时间为: 3(n+1)^2*T
当然啦,这个不是重点,重点是它的组成结构——不恢复余数除法器
虽然不是重点,但我们也先说一下,有个对比先
大致流程:
通过减法依次比较被除数和除数,判定商;若不够减,则通过回加余数恢复
缺点:控制复杂
运算要求
具体例子不说了,直接上重点
流程如下:
不恢复余数法
注意
这样一来,降低了控制的复杂程度
光说没内味,直接上例题吧!
【例题】x=0.101001, y=0.111, 求q = x÷y
解:
[x]补=0.101001,[y]补=0.111 ,[-y]补=1.001
0.101001 ;被除数x
+[-y] 1.001 ;第一步减除数y
---------------------------
1.110001 <0 q4=0 ;余数为负,商0,下步做加法
+[y] 0.0111 ;除数右移1位加(正0)
---------------------------
0.001101 >0 q3=1 ;余数为正,商1,下步做减法
+[-y] 1.11001 ;除数右移1位减(负1)
---------------------------
1.111111 <0 q2=0 ;余数为负,商0,下步做加法
+[y] 0.000111 ;除数右移1位加(正0)
---------------------------
0.000110 >0 q1=1 ;余数为正,商1,直接结束
商:q = q4.q3q2q1 = 0.101
余数:r = 0.000110
int mul(int a,int b){
int i,res=0;
int flag;
// if the result is positive
if(a>0 && b>0 || a<0 && b<0){
flag = 1;
}else{
flag = 0;
}
a = abs(a);
b = abs(b);
for(i=0;i<b;i++){
res = add(res,a);
}
if(flag)
return res;
else
return -res;
}
int div(int a,int b){
int i,count,res,quotient=0;
int flag;
// if the result is positive
if(a>0 && b>0 || a<0 && b<0){
flag = 1;
}else{
flag = 0;
}
a = abs(a);
b = abs(b);
res = a;
while(sub(res,b)>0){
res = sub(res,b);
quotient++;
}
if(flag)
return quotient;
else
return -quotient;
}
解:[x]补 = 11 0100,[y]补 = 11 1000
11 0100
+ 11 1000
------------
10 1100
∴ x + y = 10 1100,符号位为10,有溢出(负溢出)
解:
[Y]原 = 1 10001
[-Y]原 = 0 10001
[-Y]补 = 0 10001 = 0001 0001
FA:8 * (8-1) = 56
与门:8 * 8 = 64
提示:
①先将X和Y转换为纯小数;
②符号位和数值部分分开计算,仅将X和Y的绝对值部分(正数)按流程计算
解:
x = 0.11000*2^5, y = -0.11111*2^5, 相互抵消一下,其实就没了
这边我们暂时把x和y的2^5去掉,负号也暂时先放一边
[x]补=0.11000 00000,[y]补=0.11111 ,[-y]补=1.00001
0.1100000000 ;被除数x
+[-y] 1.00001 ;第一步减除数y
---------------------------
1.1100100000 <0 q6=0;余数为负,商0,下步做加法
+[y] 0.011111 ;除数右移1位加(正0)
---------------------------
0.0100010000 >0 q5=1;余数为正,商1,下步做减法
+[-y] 1.1100001 ;除数右移1位减(负1)
---------------------------
0.0000011000 >0 q4=1;余数为正,商1,下步做减法
+[-y] 1.11100001 ;除数右移1位减(负1)
---------------------------
1.1110011100 <0 q3=0;余数为负,商0,下步做加法
+[y] 0.000011111 ;除数右移1位加(正0)
---------------------------
1.1111011010 <0 q2=0;余数为负,商0,下步做加法
+[y] 0.0000011111 ;除数右移1位加(正0)
---------------------------
1.1111111001 <0 q1=0;最后一位商为0,要恢复余数
+[y] 0.0000011111 ;恢复余数时,均+[y]补
---------------------------
0.0000011000
商:q = -q6.q5q4q3q2q1 = -0.11000
余数:r = 0.0000011000 * 2^5 = 0.11000