c语言学习笔记(十二:位运算实现加减乘除四则运算

1.加法

不考虑进位情况下,位的异或运算跟*求’和’*的结果一致:

异或 1^1=0 1^0=1 0^0=0

求和 1+1=0 1+0=1 0+0=0

位的与运算跟*求’进位‘*的结果也是一致:

位与 1&1=1 1&0=0 0&0=0

进位 1+1=1 1+0=0 0+0=0

思想:

计算a+b

stp1:算出两数不考虑进位的值sum,和只考虑进位的值carry

stp2:如果进位值不为零,就a=sum,b=carry,然后重复stp1,直到carry==0

int Add(int num1, int num2) {
        int n1,n2;
        n1=(num1&num2)<<1;
        n2=num1^num2;
        while(n1&n2) {
       	  num1=n1;num2=n2;
          n1=(num1&num2)<<1;
          n2=num1^num2;
        }
        return n1|n2;
    }

递归实现

2.减法

a-b=a+(-b)

求一个数的负的操作是将其连符号位一起取反然后加1

 int subtraction(int a, int b) {
    return add(a, add(~b, 1));
}

3.乘法

乘法运算可以转化为加法运算,但是需要考虑符号问题。

1)计算绝对值乘积;

2)根据同正异负,确定符号位。

int multiply(int a, int b){ 
    // 取绝对值      
    int multiplicand = a < 0 ? add(~a, 1) : a;    
    int multiplier = b < 0 ? add(~b , 1) : b;// 如果为负则取反加一得其补码,即正数      
    // 计算绝对值的乘积      
    int product = 0;    
    int count = 0;    
    while(count < multiplier) {        
        product = add(product, multiplicand);        
        count = add(count, 1);// 这里可别用count++,都说了这里是位运算实现加法      
    }    
    // 确定乘积的符号      
    if((a ^ b) < 0) {// 只考虑最高位,如果a,b异号,则异或后最高位为1;如果同号,则异或后最高位为0;            
        product = add(~product, 1);    
    }    
    return product;
}

第二种思路:二进制乘法!

这一过程就是根据乘数的每一位为0或1时,将被乘数错位的加在积上。时间复杂度为O(logN)。

(1) 判断乘数是否为0,为0跳转至步骤(4)
(2) 将乘数与1作与运算,确定末尾位为1还是为0,如果为1,则相加数为当前被乘数;如果为0,则相加数为0;将相加数加到最终结果中;
(3) 被乘数左移一位,乘数右移一位;回到步骤(1)
(4) 确定符号位,输出结果;

int multiply(int a, int b) {  
    //将乘数和被乘数都取绝对值 
    int multiplicand = a < 0 ? add(~a, 1) : a;   
    int multiplier = b < 0 ? add(~b , 1) : b;  
     
    //计算绝对值的乘积  
    int product = 0;  
    while(multiplier > 0) {    
        if((multiplier & 0x1) > 0) {// 每次考察乘数的最后一位    
            product = add(product, multiplicand);    
        }     
        multiplicand = multiplicand << 1;// 每运算一次,被乘数要左移一位    
        multiplier = multiplier >> 1;// 每运算一次,乘数要右移一位(可对照上图理解)  
    }   
    //计算乘积的符号  
    if((a ^ b) < 0) {    
        product = add(~product, 1);  
    }   
    return product;
}

4.除法

除法运算转换成减法运算,即不停的用除数去减被除数,直到被除数小于除数时,此时所减的次数就是我们需要的商,而此时的被除数就是余数。这里需要注意的是符号的确定,商的符号和乘法运算中乘积的符号确定一样,即取决于除数和被除数,同号为证,异号为负;余数的符号和被除数一样。

int division(int a, int b)
{
	if (b == 0)return 0;
 
	bool flag = true;
	if ((a^b) > 0) flag = false;
 
	a = (a < 0) ? add(~a, 1) : a;
	b = (b < 0) ? add(~b, 1) : b;
 
	int n = 0;
	a = subtraction(a, b);
	while (a >= 0)
	{
		n = add(n, 1);
		a = subtraction(a, b);
	}
 
	if (flag)
        return add(~n, 1);
	return n;
}

采用类似二分法的思路,从除数*最大倍数开始测试,如果比被除数小,则要减去,下一回让除数的倍数减少为上一次的一半,当倍数为1时,就把被除数中所有的除数减去,并得到商。时间复杂度优化到O(logN)。

计算机是一个二元的世界,所有的int型数据都可以用[2^0, 21,…,231]这样一组基来表示(int型最高31位)。不难想到用除数的231,230,…,22,21,2^0倍尝试去减被除数,如果减得动,则把相应的倍数加到商中;如果减不动,则依次尝试更小的倍数。这样就可以快速逼近最终的结果。

2的i次方其实就相当于左移i位,为什么从31位开始呢?因为int型数据最大值就是2^31啊。

int divide(int dividend, int divisor) {
	bool flag = true;
	if ((dividend^divisor)>0) //积的符号判定
		flag = false;
 
	int x = (dividend < 0) ? add(~dividend, 1) : dividend;
	int y = (divisor < 0) ? add(~divisor, 1) : divisor;
 
	int ans = 0;
	int i = 31;
	while (i >= 0)
	{
		//比较x是否大于y*(1<<i)=(y<<i),避免直接比较,因为不确定y*(1<<i)是否溢出  
		if ((x >> i) >= y)  //如果够减
		{
			ans = add(ans, (1 << i));
			x = subtraction(x, (y << i));
		}
		i = subtraction(i, 1);
	}
	if (flag)
		return add(~ans, 1);
	return ans;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值