用位操作实现整数的加减乘除

用位操作实现整数的加减乘除

相关位操作:

由于使用位操作,所以需要进行二进制位的相关操作:

1. 按位异或 (^)

    可用于二进制数的无进位加法。
    同时,异或运算还支持结合律和交换律:
    即 a^b^c == a^(b^c)
       a^b^c == c^b^a

2. 按位或 (|)

    通常用于将某一位置一,也可以将某位的数取出
    比如 a |= 0x10 就可以将第4位置一,同时取出0~3位的数值。

3. 按位与 (&)

    通常用于将某一位置零或取出特定一位的数值
    两个二进制数之间使用还能确定哪些位数将会溢出(进位)

加法

  与十进制下的加法类似,两大重要因素包括同位的相加以及相邻位的进位,需要用到 异或 与 与 运算 

加法下我们注意两个步骤:无进位相加 以及 进位管理

  例如:13 + 19

        13
  +     19
——————————————————
        22
   +    1
——————————————————
        32

若转化为二进制:
       1101
  +   10011
——————————————————— 
      11110
 +       1
———————————————————
     100000   -> 32

     13 与 19 先不考虑进位,计算无进位相加的结果:
      1101
^    10011
————————————  
     11110  ->   30

 接下来考虑进位:
      1101
 &   10011
 ------------
     00001      

因此在第一位溢出,所以应向第二位进位
所以 13&19 << 1 即为需要进的位 即为 00010
再用需要进的位 (00010)再次进行 无进位相加 再考虑进位
重复上述过程
直至进位数等于0为止。

代码实现

int sum(int a ,int b)
{

   int ret = (a ^ b);//用于记录无进位相加
   int yichu = (a & b) << 1 ;//用于管理进位
   int temp ; //临时的中间变量
   while(yichu != 0) //如有进位,则一直进行
    {
       temp = ret; 
       ret = ret ^ yichu ;
       yichu = (temp & yichu) << 1;
        
    }
   return ret ;

}

用递归实现的优化版本 :

int sum1(int a,int b){ 

    if(a & b == 0)  
      return (a ^ b);
    else
      return sum1(a ^ b,(a & b) << 1);
}

乘法

乘法运算可以说是加法运算的延申,可用加法的循环来实现,在此处我们使用位移运算计算:
——对有符号整型来说,数的左移和右移分别相当于 乘以二的次幂 和 除以二的次幂 ,称之为算术位移

例如:
   a << 1  === a * 2;
   a >> 1  === a / 2;

那么对于不是二的次幂该如何相乘呢?我们可以用二的次幂的乘法和加法组合获得相应的结果,例如:

   a * 5  === a * 4 + 1;
   b * 7  === b * 4 + b + b + b;

通过位运算也可以得到相同的结果。

不过我们在此处运用手算乘法时的步骤,比如说 :

     十进制下乘法的手乘过程:
     17 * 19 =

          17
          19
————————————————————
         153
         17
——————————————————————
         323

    二进制下:
        10001
        10011
——————————————————————
        10001
       10001
    10001 
——————————————————————
    101000011     -->   32

*******二进制下的手算比十进制下的手算更加容易,只需要遍历其中一个因子的所有位数,若为一,则将其余一个因子左移相应位数的偏移量,即可,或者说,进行加权相加。
代码的实现即为模拟手算的过程:

int mult(int a , int b)
{   
    int flag = 1;
    if(a < 0)
        {
            a = (~a) + 1;//若为负数,则转化为正数
            flag = -flag; //控制符号正负
        }
    if(b < 0)
        {
            b = (~b) + 1;
            flag = -flag;
        }
    if(a == 0 || b == 0)
        return 0;//考虑乘数为0时
    int ret = 0;
    while(b)
        {
            if(b & 0x01)
            {
                ret = sum1(ret , a);
            }
            a <<= 1;   //进行加权左移
            b >>= 1;
        }
    if(flag == 1)
        return ret;
    else
        return -ret;
    
}

减法

可以由加法转化而来,比如 a - b == a + (-b)
此处就涉及到运用位运算将正负数转化,由于负数是以补码的形式存储的,补码可以理解位需要加几才等于0,转化可由相应的正数取反加1获得。

代码实现
int mins(int a,int b){

    return (sum1(a,(~b) + 1)) ;
}

与加法相似,这里不多做赘述。

除法

除法是减法的延续,我们可以用减法的循环来实现,但这样做未免太低效,我们可以做一些优化。
如果除数为二的次幂,则可以使用位右移来快速的实现:

int devide_2times(int a , int b)
{
    int flag = 1;
    if(a == 0 ||b == 0)
        return 0;
    if(a < 0)
        {
            a = (~a) + 1;
            flag = -flag;
        }
     if(b < 0)
        {
            b = (~b) + 1;
            flag = -flag;
        }
    while(b != 0x01)
        {
            a >>= 1;
            b >>= 1;
        }
    if(flag == 1)
        return a;
    else
        return -a;
   
}

如果除数不为二的次幂,我们可以使用循环相减,但在这里做一些优化,先找到小于被除数的最大的 因子,因子为 2^n*除数,用被除数减去该因子并在 结果值上加上2^n 在不断循环,直到n == 0为止。

代码实现

  int devide(int devided,int devider)
  {
      if(devided == 0 || devider == 0)
        return 0;
      int flag = 1;
      int n = 0;
      int ret = 0;
      if(devided < 0)
        {
            flag = -flag;
            devided = (~devided) + 1;
        }
      if(devider < 0)
        {
            flag = -flag;
            devider = (~devider) + 1;
        }
      while(devided >= devider)
        {   
            if((devided >> (30 - n)) >= devider)  
               {
                   ret = sum1(ret , (1 << (30 - n)));
                   devided = mins(devided,devider<<(30 - n));
                   
               }
            n++;
        }
        if(flag == 1)
            return ret;
        else
            return -ret;  
  }

在实现上述代码的过程中一定要注意是被除数右移,这样不会产生溢出,若将除数左移则很容易就产生溢出!!!

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值