C/C++位操作技巧

C++位操作包括两种:传统的C语言方式的位操作和C++中利用bitset容器的位操作

   
一、传统的C方式位操作:
1.基本操作:
   使用一个unsigned int变量来作为位容器。
2.操作符:
|   按位或操作符:result=exp1|exp2;当exp1和exp2中对应位中至少有一个为1时,result中对应位为1,否则为0。
&  按位与操作符::result=exp1&exp2;当exp1和exp2中对应位全为1时,result中对应位为1,否则为0。
^  按位异或或操作符:result=exp1^exp2;当exp1和exp2中对应位不相同时,result中对应位为1,否则为0。
~  反转操作符:将位容器中的所有位都反转,1变为0,0变为1。
<< 按位左移操作符:exp<<n,将容器中所有的位向左移n位,空出的位用0填充。
>> 按位右移操作符:exp>>n,将容器中所有的位向右移n位,空出的位用0填充。
|=,&=,^= 分别对应|&^三种操作符的复合操作符。
3.常用操作
   这里我们假设有一个result的unsigned int变量用来储存32个学生的成绩(通过和不通过分别用0和1),这样result就有33位(result从右至左,从0开始计算位数,在这个例子中0位被浪费)。
(a) 将第27位设置为及格(设作1)其他位不变:
   result|=(1<<27) //任意的位值与1作按位或操作其值为1,而与0作按位与操作其值不变
(b) 将第27位设置成不及格(设为0)。
   result&=~(1<<27) //任意的位值与0作按位与操作其值为0,而与1作按位与操作其值不变
(c) 反转第27位的值。
   result^=(1<<27) //任意的位值与1作按位异或操作其值为1,而与0作按位异与操作其值不变
 
二、C++中的bitset容器
1.头文件:
  #include <bitset>
2.声明一个容器:
 (a)声明一个指定位数的空容器(所有位设为0): bitset<int> bits;
 (b)声明一个指定位数并将指定的几个位初始化为相应值的容器: bitset<n> bits(int);
     bitdet<int> bits(string&)
总结:bitset模板类中类型参数传递容器的位数,而构造函数参数通过一个int或一个string&值来从右至左初始化容器中的相应值。
3.bitset的基本用法:
4.bitset与传统C位操作及字符串的转换
   可以通过to_string()成员将容器转输出为一个string字符串,另外还可以用to_long()成员将容器输出到传统的用于C风格的位容器中。如:
  unsigned long bits = bits.to_long();
  sting str(bits.to_string());
5.bitset支持所有的位操作符。
/

检测一个无符号数是不为2^n-1(^为幂):   x&(x+1)  
    
  将最右侧0位改为1位:   x   |   (x+1)  
    
  二进制补码运算公式:  
  -x   =   ~x   +   1   =   ~(x-1)  
  ~x   =   -x-1    
  -(~x)   =   x+1  
  ~(-x)   =   x-1  
  x+y   =   x   -   ~y   -   1   =   (x|y)+(x&y)    
  x-y   =   x   +   ~y   +   1   =   (x|~y)-(~x&y)    
  x^y   =   (x|y)-(x&y)  
  x|y   =   (x&~y)+y  
  x&y   =   (~x|y)-~x  
    
  x==y:         ~(x-y|y-x)  
  x!=y:         x-y|y-x  
  x<   y:         (x-y)^((x^y)&((x-y)^x))  
  x<=y:         (x|~y)&((x^y)|~(y-x))  
  x<   y:         (~x&y)|((~x|y)&(x-y))//无符号x,y比较  
  x<=y:         (~x|y)&((x^y)|~(y-x))//无符号x,y比较  
    
    
  使用位运算的无分支代码:  
    
  计算绝对值  
  int   abs(   int   x   )    
  {  
  int   y   ;  
  y   =   x   >>   31   ;  
  return   (x^y)-y   ;//or:   (x+y)^y  
  }  
    
  符号函数:sign(x)   =   -1,   x<0;   0,   x   ==   0   ;   1,   x   >   0  
  int   sign(int   x)  
  {  
  return   (x>>31)   |   (unsigned(-x))>>31   ;//x=-2^31时失败(^为幂)  
  }  
    
  三值比较:cmp(x,y)   =   -1,   x<y;   0,   x==y;   1,   x   >   y  
  int   cmp(   int   x,   int   y   )  
  {  
  return   (x>y)-(x-y)   ;  
  }  
    
  doz=x-y,   x>=y;   0,   x<y  
  int   doz(int   x,   int   y   )  
  {  
  int   d   ;  
  d   =   x-y   ;  
  return   d   &   ((~(d^((x^y)&(d^x))))>>31)   ;  
  }  
    
  int   max(int   x,   int   y   )    
  {  
  int   m   ;  
  m   =   (x-y)>>31   ;    
  return   y   &   m   |   x   &   ~m   ;  
  }  
    
  不使用第三方交换x,y:  
  1.x   ^=   y   ;   y   ^=   x   ;   x   ^=   y   ;  
  2.x   =   x+y   ;   y   =   x-y   ;   x   =   x-y   ;  
  3.x   =   x-y   ;   y   =   y+x   ;   x   =   y-x   ;  
  4.x   =   y-x   ;   x   =   y-x   ;   x   =   x+y   ;    
    
  双值交换:x   =   a,   x==b;   b,   x==a//常规编码为x   =   x==a   ?   b   :a   ;  
  1.x   =   a+b-x   ;  
  2.x   =   a^b^x   ;  
    
  下舍入到2的k次方的倍数:  
  1.x   &   ((-1)<<k)  
  2.(((unsigned)x)>>k)<<k  
  上舍入:  
  1.   t   =   (1<<k)-1   ;   x   =   (x+t)&~t   ;  
  2.t   =   (-1)<<k   ;   x   =   (x-t-1)&t   ;  
    
  位计数,统计1位的数量:  
  1.  
  int   pop(unsigned   x)  
  {  
  x   =   x-((x>>1)&0x55555555)   ;  
  x   =   (x&0x33333333)   +   ((x>>2)   &   0x33333333   )   ;  
  x   =   (x+(x>>4))   &   0x0f0f0f0f   ;  
  x   =   x   +   (x>>8)   ;  
  x   =   x   +   (x>>16)   ;  
  return   x   &   0x0000003f   ;  
  }  
  2.  
  int   pop(unsigned   x)   {  
  static   char   table[256]   =   {   0,1,1,2,   1,2,2,3,   ....,   6,7,7,8   }   ;  
  return   table[x&0xff]+table[(x>>8)&0xff]+table[(x>>16)&0xff]+table[(x>>24)]   ;  
  }  
    
  奇偶性计算:  
  x   =   x   ^   (   x>>1   )   ;  
  x   =   x   ^   (   x>>2   )   ;  
  x   =   x   ^   (   x>>4   )   ;  
  x   =   x   ^   (   x>>8   )   ;  
  x   =   x   ^   (   x>>16   )   ;  
  结果中位于x最低位,对无符号x,结果的第i位是原数第i位到最左侧位的奇偶性  
    
    
  位反转:  
  unsigned   rev(unsigned   x)  
  {  
  x   =   (x   &   0x55555555)   <<   1   |   (x>>1)   &   0x55555555   ;  
  x   =   (x   &   0x33333333)   <<   2   |   (x>>2)   &   0x33333333   ;  
  x   =   (x   &   0x0f0f0f0f)   <<   4   |   (x>>4)   &   0x0f0f0f0f   ;  
  x   =   (x<<24)   |   ((x&0xff00)<<8)   |   ((x>>8)   &   0xff00)   |   (x>>24)   ;  
  return   x   ;  
  }  
    
  递增位反转后的数:  
  unsigned   inc_r(unsigned   x)  
  {  
  unsigned   m   =   0x80000000   ;  
  x   ^=   m   ;  
  if(   (int)x   >=   0   )    
  do   {   m   >>=   1   ;   x   ^=   m   ;   }   while(   x   <   m   )   ;  
  return   x   ;  
  }  
    
  混选位:  
  abcd   efgh   ijkl   mnop   ABCD   EFGH   IJKL   MNOP->aAbB   cCdD   eEfF   gGhH   iIjJ   kKlL   mMnN   oOpP  
  unsigned   ps(unsigned   x)  
  {  
  unsigned   t   ;  
  t   =   (x   ^   (x>>8))   &   0x0000ff00;   x   =   x   ^   t   ^   (t<<8)   ;  
  t   =   (x   ^   (x>>4))   &   0x00f000f0;   x   =   x   ^   t   ^   (t<<4)   ;  
  t   =   (x   ^   (x>>2))   &   0x0c0c0c0c;   x   =   x   ^   t   ^   (t<<2)   ;  
  t   =   (x   ^   (x>>1))   &   0x22222222;   x   =   x   ^   t   ^   (t<<1)   ;  
  return   x   ;  
  }  
    
  位压缩:  
  选择并右移字x中对应于掩码m的1位的位,如:compress(abcdefgh,01010101)=0000bdfh  
  compress_left(x,m)操作与此类似,但结果位在左边:   bdfh0000.  
  unsigned   compress(unsigned   x,   unsigned   m)  
  {  
  unsigned   mk,   mp,   mv,   t   ;  
  int   i   ;  
    
  x   &=   m   ;  
  mk   =   ~m   <<   1   ;  
  for(   i   =   0   ;   i   <   5   ;   ++i   )   {  
  mp   =   mk   ^   (   mk   <<   1)   ;  
  mp   ^=   (   mp   <<   2   )   ;  
  mp   ^=   (   mp   <<   4   )   ;  
  mp   ^=   (   mp   <<   8   )   ;  
  mp   ^=   (   mp   <<   16   )   ;  
  mv   =   mp   &   m   ;  
  m   =   m   ^   mv   |   (mv   >>   (1<<i)   )   ;  
  t   =   x   &   mv   ;  
  x     =   x   ^   t   |   (   t   >>   (   1<<i)   )   ;  
  mk   =   mk   &   ~mp   ;  
  }  
  return   x   ;  
  }  
    
    
  位置换:  
  用32个5位数表示从最低位开始的位的目标位置,结果是一个32*5的位矩阵,  
  将该矩阵沿次对角线转置后用5个32位字p[5]存放。  
  SAG(x,m)   =   compress_left(x,m)   |   compress(x,~m)   ;  
  准备工作:  
  void   init(   unsigned   *p   )   {  
  p[1]   =   SAG(   p[1],   p[0]   )   ;  
  p[2]   =   SAG(   SAG(   p[2],   p[0]),   p[1]   )   ;  
  p[3]   =   SAG(   SAG(   SAG(   p[3],   p[0]   ),   p[1]),   p[2]   )   ;  
  p[4]   =   SAG(   SAG(   SAG(   SAG(   p[4],   p[0]   ),   p[1])   ,p[2]),   p[3]   )   ;  
  }  
  实际置换:  
  int   rep(   unsigned   x   )   {  
  x   =   SAG(x,p[0]);  
  x   =   SAG(x,p[1]);  
  x   =   SAG(x,p[2]);  
  x   =   SAG(x,p[3]);  
  x   =   SAG(x,p[4]);  
  return   x   ;  
  }  
    
  二进制码到GRAY码的转换:  
  unsigned   B2G(unsigned   B   )  
  {  
  return   B   ^   (B>>1)   ;  
  }  
  GRAY码到二进制码:  
  unsigned   G2B(unsigned   G)  
  {  
  unsigned   B   ;  
  B   =   G   ^   (G>>1)   ;  
  B   =   G   ^   (G>>2)   ;  
  B   =   G   ^   (G>>4)   ;  
  B   =   G   ^   (G>>8)   ;  
  B   =   G   ^   (G>>16)   ;  
  return   B   ;  
  }  
    
  找出最左0字节的位置:  
  int   zbytel(   unsigned   x   )  
  {  
  static   cahr   table[16]   =   {   4,3,2,2,   1,1,1,1,   0,0,0,0,   0,0,0,0   }   ;  
  unsigned   y   ;  
  y   =   (x&0x7f7f7f7f)   +   0x7f7f7f7f   ;  
  y   =   ~(y|x|0x7f7f7f7f)   ;  
  return   table[y*0x00204081   >>   28]   ;//乘法可用移位和加完成  
  }  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值