C语言:位运算

来源:互联网 发布:linux dhcp设置dns 编辑:程序博客网 时间:2024/06/07 23:38

移位运算

清零取反要用与,某位置一可用或若要取反和交换,轻轻松松用异或
  • 原操作数:s;掩码:mask

  • 与 (&) …… 0 & 0 = 0……. 1 & 0 = 0 ……0 & 1 = 0……1 & 1 = 1 ( AND)

1. 清零特定位: (mask中特定位置0,其它位为1,s=s&mask)2  取某数中指定位 (mask中特定位置1,其它位为0,s=s&mask)
  • 或 (|) ……. 0 | 0 = 0……… 1 | 0 = 1…… 0 | 1 = 1…….. 1 | 1 = 1 ( OR )
1. 常用来将源操作数某些位置1,其它位不变。 (mask中特定位置1,其它位为0 s=s|mask)
  • 异或(^)…….0 ^ 0 = 0……. 1 ^ 0 = 1… 0 ^ 1 = 1…. 1 ^ 1 = 0 ( XOR )
 1 使特定位的值取反(mask中特定位置1,其它位为0 s=s^mask)1111010^00001111=01110101 20^,保留原值:00001010^00000000=00001010 3 交换:34 a=a^b :a=011 ^ b=100=>a=111 (a=7) b=b^a :b=100 ^ a=111=>b=011(b=3a=a^b :a=111 ^ b=011=>a=100a=4
  • 左移运算符m<<n表示吧m左移n位,左移n位的时候,最左边的n位将被丢弃,同时在最右边补上n个0.比如:
00001010 << 2 = 00101000
  • 右移运算符m>>n表示把m右移n位。右移n位的时候,最右边的n位将被丢弃。逻辑右移和算术右移。逻辑右移在左端补k个0;算术右移是在左端补k个最高有效位的值。
00001010 >> 2 = 0000001010001010 >> 3 = 11110001
  • 把整数右移一位和把整数除以2在数学上是等价的。
  • 把整数左移一位和把整数乘以2在数学上是等价的。
a << = 1 ; //a左移一位等效于a = a * 2;

  • 不同长度数据,进行位运算
右端对齐,如是正数:左端补满0;b是负数,左端布满1;

例子

  • 取一个整数a的4-7位:
1. 先使a右移4位:a>>42. 设置低4位全是1,其余是0;~(~0<<4);~的优先级高3. (a>>4)& ~(~0<<4)
  • 循环移位
//a进行循环右移将a右循环n位,将a中的原来的左边(16-n)右移,右端的n位移动到最左端1. 将a的右端n位,放到b的高n位:b=a<<(16-n)2. a右移n位,左边n位补零: c=a>>n;3. 将c与b按位或运算,c=c|b

位运算技巧

  • 计算一个数的二进制中1的个数
//通过与初始值为1的标志位进行与运算,判断最低位是否为1;然后将标志位左移,判断次低位是否为1;一直这样计算,直到将每一位都判断完毕。int countOf1(int num)  {      int count = 0;      unsigned int flag = 1;      while(flag)      {          if(num & flag)          {              count++;          }          flag = flag << 1;      }      return count;  }  //还有一种方法,一个整数减一,可以得到该整数的最右边的1变为0,这个1右边的0变为1。对这个整数和整数减一进行与运算,将该整数的最右边的1变为0,其余位保持不变。直到该整数变为0,进行的与运算的次数即为整数中1的个数。int countOf1_2(int num)  {      int count = 0;      while(num)      {          num = num & (num - 1);          count++;      }      return count;  }  
  • 判断一个数是否是2的n次方
一个数是2的n次方,则这个数的最高位是1,其余位为0。根据上一题的第二种解法可以很容易得到解决方案。将这个整数与整数减一进行与运算,如果得到的结果为零,可证明该数为2的n次方。/*     判断一个数是否为2的n次方(一个数为2的n次方,则最高位为1,其余位为0) */  bool is2Power(int num)  {      bool flag = true;      num = num & (num - 1); //计算num和num - 1的与的结果      if(num) //如果结果为0,则不是2的n次方      {          flag = false;      }      return flag;  }  
  • 整数n经过多少步可以变为整数m
n和m的异或结果可以得知两数不同位的个数,再调用计算一个数中1的个数的方法,即可得到结果。/*     求解n变化为m,需要进行的操作步数 */  int countChange(int n,int m)  {      n = n ^ m; //求n和m的异或,再计算结果中1的个数      return countOf1_2(n);  }  
  • 获得最大的int值
/*     获取最大的int     得到结果:2147483647 */  int getMaxInt()  {      return (1 << 31) - 1;  }  /*     使用g++编译,出现warning: left shift count is negative */  int getMaxInt_2()  {      return (1 << -1) - 1;  }  int getMaxInt_3()  {      return ~(1 << 31);  }  /*     在不了解int的长度情况下使用 */  int getMaxInt_4()  {      return ((unsigned int) -1) >> 1;   }  
  • 获得最小的int值
与获得最大的int方法类似。/*     求最小int     得到结果:-2147483648 */  int getMinInt()  {      return 1 << 31;  }  /*     同样在g++下编译,出现warning: left shift count is negative */  int getMinInt_2()  {      return 1 << -1;  }  
  • 获得最大的long
/*     求最大long     得到结果:9223372036854775807 */  long getMaxLong()  {      return ((unsigned long) -1) >> 1;  } 
  • 判断一个数的奇偶性
判断奇偶性,实质是判断最后一位是否是1.bool isOdd(int num)  {      return num & 1 == 1;  }  
  • 交换两个数(不借助第三变量)
/*     不适用临时变量,交换两个数     a = a ^ b     b = b ^ a     a = a ^ b */  void mySwap(int* a,int* b)  {      (*a) ^= (*b) ^= (*a) ^= (*b);  }  
  • 求一个数的绝对值
下面的方法实现的基础是将n右移31位,可以获得n的符号。/*     取绝对值     n右移31位,可以获得n的符号。若n为正数,得到0;若n为负数,得到 -1 */  int myAbs(int n){      return (n ^ n >> 31) - (n >> 31);  }  
  • 求两个数的平均值
第一种方法较为普遍且简单,不多说了。第二种方法,需要知道的是,( m ^ n ) >> 1得到的结果是m和n其中一个数的有些位为1的值的一半,m & n得到的结果是m 和n都为1的那些位,两个结果相加得到m和n的平均数。/*     求m和n的平均数 */  int getAverage(int m,int n){      return (m + n) >> 1;  }  /*     求m和n的平均数     (m ^ n) >> 1 -> 获得m和n两个数中一个数的某些位为1的一半     m & n -> 获得m和n两个数中都为1的某些位 */  int getAverage_2(int m,int n){      return ((m ^ n) >> 1) + (m & n);  }  
  • 求解倒数第m位相关问题
/*     获取n的倒数第m位的值(从1开始计数) */  int getMthByTail(int n,int m){      return (n >> (m - 1)) & 1;  }  /*     将n的倒数第m位设为1 */  int setMthByTail21(int n,int m)  {      return n | (1 << (m - 1));  }  /*     将n的倒数第m位设为0 */  int setMthByTail20(int n,int m)  {      return n & ~(1 << (m - 1));  } 

四则运算的位表示

  • 加法运算
int Add(int a, int b)  {      int ans;      while(b)      {   //直到没有进位          ans = a^b;        //不带进位加法          b = ((a&b)<<1);   //进位          a = ans;      }      return a;  }  
  • 减法运算
int negtive(int a)   //取补码  {      return Add(~a, 1);  }  int Sub(int a, int b)  {      return Add(a, negtive(b));  }   
  • 乘法运算:原理上还是通过加法计算。将b个a相加,注意下面实际的代码。
int Multi(int a, int b)  {      int ans = 0;      while(b)      {          if(b&1)              ans = Add(ans, a);          a = a << 1;          b = b >> 1;      }      return ans;  }
  • 正数除法:
//除法就是由乘法的过程逆推,依次减掉(如果x够减的)y^(2^31),y^(2^30),...y^8,y^4,y^2,y^1。减掉相应数量的y就在结果加上相应的数量。  int Divide(int a, int b)  {      int coun = 0;      while(a >= b)      {          a = Minus(a, b);          coun = Add(coun, 1);      }      return coun;  }
  • 判断是否是0.正数,负数
  //判断是否是负数,0,正数  int isneg(int a)  {      return a & 0x8000;  }  int iszero(int a)  {      return !(a & 0xFFFF);  }  int ispos(int a)  {      return (a&0xFFFF) && !(a&0x8000);  }


31.1-11

写出一个β 位整数除以一个短整数的有效算法,以及求一个β 为整数除以一个短整数的余数有效算法,给出的是时间是O(β2)

http://blog.csdn.net/z84616995z/article/details/21945197
http://blog.163.com/luowei505050@126/blog/static/1199072062011102415243718/

//位运算#include <iostream>  using namespace std;  //位运算乘法乘法就是将乘数写成(2^0)*k0 + (2^1)*k1 + (2 ^2)*k2 + ... + (2^31)*k31,其中ki为01,然后利用位运算和加法就可以了。int bit_Multiplication(int a,int b)  {     int ans=0;       for(int i=1;i;i<<=1,a<<=1)     {          if(b&i)          {              ans+=a;          }     }     return ans;}//位运算的除法  除法就是由乘法的过程逆推,依次减掉(如果x够减的)y^(2^31),y^(2^30),...y^8,y^4,y^2,y^1。减掉相应数量的y就在结果加上相应的数量。int bit_Division1(int x,int y)  {      int ans=0;      for (int i=31;i>=0;i--)      {     //比较x是否大于y的(1<<i)次方,避免将x与(y<<i)比较,因为不确定y的(1<<i)次方是否溢出          if ((x>>i)>=y)          {              ans+=(1<<i);              x-=(y<<i);          }      }      return ans;  } //计算整数的二进制位数  int bit_num(int d)  {     int i=0;     while (d)     {         d>>=1;         i++;     }     return i;  }  //位运算的除法 计算商  int bit_Division2_quotient(int x,int y)  {      int c2=bit_num(x),c1=bit_num(y),quotient=0;      for (int i=c2-c1;i>=0;i--)//i=c2-c1防止除数y移位后超过无符号整数最大值 时间复杂度O(c2-c1)      {          unsigned int a=(y<<i);//有了i=c2-c1保证了y<<i不会溢出 a有c1+c2-c1=c2位          if (a<=x)          {              quotient+=(1<<i);              x-=a;          }      }      //总的时间复杂度为 O(c2)=O(x的二进制位数)=O(b^2) b为除数的十进制位数      return quotient;  }  //位运算的除法 计算余数 与计算商一样,只是返回值不同  int bit_Division2_Remainder(int x,int y)  {      int c2=bit_num(x),c1=bit_num(y),quotient=0;      for (int i=c2-c1;i>=0;i--)//i=c2-c1防止除数y移位后超过无符号整数最大值 时间复杂度O(c2-c1)      {          unsigned int a=(y<<i);//有了i=c2-c1保证了y<<i不会溢出 a有c1+c2-c1=c2位          if (a<=x)          {              quotient+=(1<<i);              x-=a;          }      }      //总的时间复杂度为 O(c2)=O(x的二进制位数)=O(b^2) b为除数的十进制位数      return x;  }  void main()  {      cout<<bit_Multiplication(350,43)<<endl;      cout<<bit_Division1(350,43)<<endl;      cout<<"商:"<<bit_Division2_quotient(350,43)<<endl;      cout<<"余数:"<<bit_Division2_Remainder(350,43)<<endl;  }  

31.1-12

0 0
原创粉丝点击