位运算---只使用位运算实现整数的加减乘除

【题目】

  给定两个32位整数a和b,可正、可负、可0.不能使用算术运算符,分别实现a和b的加减乘除。

【要求】

  如果给定的a和b执行加减乘除的某些结果本来就会导致数据的溢出,那么你实现的函数不必对那些结果负责。

【基本思路】

 加法运算

  使用位运算实现加法运算主要分为两个部分。先计算完全不考虑进位进行相加的结果,再计算只考虑进位的产生值。将两个结果相加就是最终的结果。

  例如:
  a:    001010101
  b:    000101111

  • 首先不考虑进位进行相加,结果为001111010,该结果其实就是a ^ b。

  • 再考虑进位的产生值,结果为000001010,该结果其实就是(a & b)<< 1。

  • 将1、2产生的结果再相加,此时依然要考虑两部分:不考虑进位和只考虑进位。

  • 一直重复上述步骤,直到进位产生的值全部消失。

下面是使用c++实现的代码。

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

 减法运算

  实现a - b只要实现a + (-b)即可。所以只要将a和b的相反数调用add函数就行。根据二进制数在机器中表达的规则,得到一个数的相反数,就是这个数的二进制数表达取反加1(补码)的结果。代码如下:

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

 乘法运算

  用位运算实现乘法运算。a × b的结果可以写成 a20b0+a21b1+...a2ibi+...a231b31 . 其中 bi 为0或1表示整数b的二进制表达中第i位的值(从右往左数)。该过程有点类似于求整数的N次方问题。具体实现参照如下代码:

int multi(int a, int b)
{
    int res = 0;
    while(b != 0)
    {
        if(b & 1 == 1)
            res = add(res, a);
        a <<= 1;
        b >>= 1;
    }
    return res;
}

 除法运算

  用位运算实现除法运算其实就是乘法的逆运算。定义 res 表示除法的结果。首先将a向右移位31位,然后看能不能容下b,如果能,说明 a/231 可以包含一个b,等价于a可以包含一个 b231 ,令res的第31位为1,此时a的值应该为 ab231 ;如果不能容下b,令res的第31位为0,a的值不变;接下来将a向右移位30位是否能容下b……重复步骤直到 ab2i=0
  以上过程只适用于a和b都不是负数的情况下,当a或b为负数时,可以先将a和b转成正数,计算完之后再判断res的真实符号就行。
  除法实现到这一步已经可以解决绝大多数情况了。但是我们知道,32位最小整数的绝对值要比最大整数大,所以如果a或b等于最小值,是不能转换成相对应的正数的。这时候需要分情况考虑:

  • 如果a和b都为最小值,直接返回1

  • 如果a不为最小值,而b为最小值,那么a/b = 0,直接返回0

  • 如果a为最小值,而b不为最小值。这时我们对a无能为力,但是我们可以让a增大一点点,计算出一个结果然后再修正一下就可以得到最终的结果。处理过程如下:

      <1>计算 (a+1)/b ,结果记为c
      <2>计算 cb
      <3>计算 (acb)/b ,结果记为rest
      <4>计算 c+rest

代码实现如下:


int divide(int a, int b)
{
    if(b == 0)
        cout<<"Input Error!"<<endl;
    else if(a == INT_MIN && b == INT_MIN)
        return 1;
    else if(b == INT_MIN)
        return 0;
    else if(a == INT_MIN)
    {    
        int c = div(a+1, b);
        return add(c, div(minus1(a, multi(c, b)), b));
    }
    else
        return div(a, b);
}


int div(int a, int b)
{
    a = a >= 0? a : add(~a, 1);
    b = b >= 0? b : add(~b, 1);
    int res = 0;
    for(int i=31; i>-1; i=minus1(i, 1))
    {
        if((a>>i) >= b)
        {
            res = res | (1<<i);
            a = minus1(a, b << i);
        }
    }
    return a >= 0 && b >= 0? add(~res, 1) : res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值