用位运算实现四则运算

四则运算的位运算实现

以C语言为例由位运算实现四则运算
位运算简单介绍


加法运算

12+89=101
将上述加法运算分解为如下过程

步骤1:不考虑进位的运算 12+88 = 01,转到步骤2
步骤2:仅考虑进位的运算 12+88 = 100,转到步骤3
步骤3:如果仅考虑进位的运算结果不为0,将两次运算的结果作为加数,重复步骤1

以二进制考虑
0000 1100 + 0101 1001 = 0110 0101

不考虑进位的运算 0000 1100 + 0101 1001 = 0101 0101
仅考虑进位的运算 0000 1100 + 0101 1001 = 0001 0000
将上述结果相加 0101 0101 + 0001 0000

不考虑进位的运算 0101 0101 + 0001 0000 = 0100 0101
仅考虑进位的运算 0101 1001 + 0001 0000 = 0010 0000
将上述结果相加 0100 0101 + 0010 0000

不考虑进位的运算 0100 0101 + 0010 0000 = 0110 0101
仅考虑进位的运算 0100 0101 + 0010 0000 = 0000 0000
终止

结果为 0110 0101


观察可知,不考虑进位的运算即是异或,仅考虑进位的运算即是与运算再左移一位

那么加法可以写作

int add(int sum, int carry){
	if(carry==0)return sum;
	return add(sum ^ carry, (sum & carry)<<1);
}

或者

int add(int a, int b){
	int sum, carry;
	do{
		sum = a ^ b;
		carry = (a & b)<<1;
		a = sum;
		b = carry;
	}while(carry);
	return sum;
}

上述操作步骤上就是机器实现加法运算的过程


减法运算

学习计算机基础后,我们应该知道,计算机中使用补码来表示数值
正数的原码、反码与补码相同
负数将原码除符号位取反后得到反码,反码加1得到补码
一个数加上另一个数的补码即相当于减去这个数

不难得到

int minus(int num, int dead){
	return add(num,add(~dead,1));
}

乘法运算

用加法实现乘法

首先

int abs(int num){
	return num<0?add((~num),1):num;
}

然后

int multiply(int a, int b){
	if(a==0||b==0)return 0;
	int flag_a = (a>>31)&1;
	int flag_b = (b>>31)&1;//取符号位
	a = abs(a);
	b = abs(b);//计算绝对值
	int min = a>b?b:a;
	int max = a>b?a:b;
	int res = 0;
	while(min){
		min = minus(min,-1);
		res = add(res,max);
	}//用加法实现乘法
	if(flag_a!=flag_b){
		res = add((~res),1);
	}//判断符号
	return res;
}

优化
上述思路选取小值作为加法次数
但是加上成千上万次还是太多了
实际上 如下图
在这里插入图片描述
借助取位操作与左移操作
只需将左移过的三个1010相加即可
也就是说 对于32位的int来说 最多做31次加法就足够了

int multiply(int a, int b){
	if(a==0||b==0)return 0;
	int flag_a = (a>>31)&1;
	int flag_b = (b>>31)&1;
	a = abs(a);
	b = abs(b);
	int res = 0;
	int cnt = 0;//位数 
	while(cnt<32){
		if(a&1){//取最后一位判断
			res = add(res,b<<cnt);
		}
		a = a>>1;//a右移 舍弃乘过的一位
		cnt = add(cnt,1);
	}
	if(flag_a!=flag_b){
		res = add((~res),1);
	}
	return res;
}

除法运算

同样的思路,用减法实现除法

int devide(int num, int dev){
	int flag = ((num ^ dev)<0);
//	if(dev==0)return flag; 不做除零处理 
	num = abs(num);
	dev = abs(dev);
	int cnt = 0;
	while(num>=dev){
		num = minus(num, dev);
		cnt = add(cnt, 1);
	}
	if(flag){
		cnt = (~cnt) + 1;
	}
	return cnt;
}

优化
每次减去一个被除数,可不可以一次减去多个被除数呢

如下

int devide(int num, int dev){
	int cnt = 0;
	int flag = (num^dev)<0;
	num = abs(num);
	dev = abs(dev);
	for(int i = 31;i>=1;i=add(i,1)){
		while((num>>i)>=dev){//num 大于 dev乘以2的i次方   避免越界所以用num>>i 
			num = minus(num,dev<<i);
			cnt = add(cnt, 1<<i);
		}
		if(num<dev){
			break;
		}
	}
	if(flag){
		cnt = add((~cnt),1);
	}
	return cnt;
}

用优化过的除法用99999999除以1,用了4s+。。。

好难过。。于是我又加了除数为1的判断


2019/4/14

posted @ 2019-04-14 20:16  kafm  阅读(103)  评论(0编辑  收藏  举报