zoukankan      html  css  js  c++  java
  • 用位运算实现四则运算

    四则运算的位运算实现

    以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

  • 相关阅读:
    UVA 10827 Maximum sum on a torus 最大矩阵和
    UVA 11100 The Trip, 2007 水题一枚
    编程之美2015测试赛 题目1 : 同构
    UVA 10801 Lift Hopping Floyd
    UVA 11389 The Bus Driver Problem 贪心水题
    UVA 11039 Building designing 贪心
    UVA 11636 Hello World! 水题
    poj 3070 Fibonacci 矩阵快速幂
    hdu 1757 A Simple Math Problem 矩阵快速幂
    了解常见的 Azure 灾难
  • 原文地址:https://www.cnblogs.com/kafm/p/12721819.html
Copyright © 2011-2022 走看看