C/C++位操作技巧

检测一个无符号数是不为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)]   ;   
  }   
3.求下面函数的返回值(microsoft)
int func(x)
{
      int countx = 0;
      while(x)
      {
             countx++;
             x = x&(x-1);
      }
      return countx;
}
这个函数的功能就是返回数中1的个数。
假定x = 9999。 答案:8
思路:将x转化为2进制,看含有的1的个数。
用最简单的方法判断一个LONG整形的数A是2^n
答案:(n-1)&n
  
    
  奇偶性计算:   
  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]   ;//乘法可用移位和加完成   
  }   

 

//C++位操作运算符程序实例
#include <iostream.h>
void main()

    unsigned int a(0x2a),b(18);
    a&=b;
    cout<<a<<endl;
    a^=a;
    cout<<a<<endl;
    int i(-8),j(2);
    i>>=j;     //分析1
    cout<<i<<endl;
    i|=~j^j;    //分析2
    cout<<i<<','<<j<<endl;
    j&=~i+1;;
    cout<<i<<','<<j<<endl;
}

运行结果如下:
2
0
-2
-1,2
-1,0

分析1:
i>>=j;  ///意为对i(-8)进行右移位j(2)位,然后赋值给i.
-8的原码:10000000 00000000 00000000 00001000(按位取反,得反码)
-8的反码:11111111 11111111 11111111 11110111(加1,得补码)
-8的补码:11111111 11111111 11111111 11111111
右移两位,移出去的空格全补符号位1,得
结果的补码:11111111 11111111 11111111 11111110(减1得反码)
结果的反码:11111111 11111111 11111111 11111101(按位求反,得原码)
结果的原码:10000000 00000000 00000000 00000010(换成十进制:-2)

分析2:
i|=~j^j;
优先级高->低排:~,^,|=
先计算(~j)^j:11111111 11111111 11111111 11111111
然后:i=i|全1
得i=11111111 11111111 11111111 11111111(减1得反码)
11111111 11111111 11111111 11111110(按位求反得原码)
10000000 00000000 00000000 00000001(换成十进制:-1)

3./* 
 * minusOne - return a value of -1 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 2
 *   Rating: 1
 */
int minusOne(void
) {
/* In memory, -1 is represented as all 1 at every bit, so we got ~0*/
  
return ~0;
}

/* 
 * TMax - return maximum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmax(void) {
/* 
 * The largest positive integer is 01111111111111111111111111111111,
 * it's two's complement is  1000000000000000000000000000000
 */
  
return ~(1<<31);
}

/* 
 * bitXor - x^y using only ~ and & 
 *   Example: bitXor(4, 5) = 1
 *   Legal ops: ~ &
 *   Max ops: 14
 *   Rating: 2
 */
int bitXor(int xint y) {
/* 
 *   Use x&y to find both 1 and ~x&~y to find both 0,the result of them at corresponding bit
 *   will be 1. ~(result1|result2) is the final result we want. Bring them into the bitOr above,
 *   we got ~(x&y)&~(~x&~y)
 */
   
return ~(x&y)&~(~x&~y);
}

/* 
 * getByte - Extract byte n from word x
 *   Bytes numbered from 0 (LSB) to 3 (MSB)
 *   Examples: getByte(0x12345678,1) = 0x56
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 6
 *   Rating: 2
 */
int getByte(int xint n) {
/* Right shift x and makes higher part zero. n<<3 bits to shift  */
   
return (x>>(n<<3))&(0x000000FF);
}
/* 
 * isEqual - return 1 if x == y, and 0 otherwise 
 *   Examples: isEqual(5,5) = 1, isEqual(4,5) = 0
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int isEqual(int xint y) {
/* If x is equal to y , every bit of x^y will be 0, !0 will be 1  */
  
return !(x^y);
}

/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
/* This is just the way how to transform a positive to a nagative */ 
  
return ~x+1;
}

/* 
 * isPositive - return 1 if x > 0, return 0 otherwise 
 *   Example: isPositive(-1) = 0.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 8
 *   Rating: 3
 */
int isPositive(int x) {
/* 
 *   Use x>>31 to get the sign bit, when x is positve ~(x>>31) is 1, else it's 0.
 *   !x to verify whether x is 0, if it's 0, !x will be 1, and !!x will be 0.
 *   So if x is zero, it will return 0, and if x is not zero, it will return the first part.
 *   That's what we want
 */  
  
return ~(x>>31)&(!!x);
}

4.下面这两个程序是有关位的封装的类:(是C++标准委员会的主席写的)
//BitBuffer是由vector<unsigned char>来实现的
a. class BitBuffer{
public:
        BitBuffer():buf_(0),size(0){}
        //
添加p所指的位缓冲区头部的num个位
        void Append(unsigned char* p, size_t num){
              int bits = numeric_limits<unsigned char>::digit;//
这是得到unsigned char的位数
              //
第一个目标字节和该字节中的位偏移
              int dst = size_/bits;
              int off = size_%bits;
              while(buf_.size() <
size_+num/bits + 1;
                     buf_.push_back(0);//
鉴于下面的算法,这里的push_back必须为0
              for(int i = 0; i < (num+bits-1)/bits; ++i){
                     unsigned char mask = FirstBits(num – bits*i);
                     buf_[dst+i] |= (*(p+i) & mask) >> off;//
把当前字节的前bits-off位写入目 标字节剩下的bits-off位空间中
                     if( off > 0)
                            buf_[dst+i+1] = (*(p+i) & mask) <<(bits – off);//
把当前字节的后off位写入目标字节下一字节的前off个空位中。
              }
              size_+=num;
        }
         //查询正被使用的位数目(初始为0
        size_t Size() const{
                     return size_;
        }
         //
从第start位(位的编号从0开始)开始获取num个位,并将结果存入dst所指的缓冲区(从缓冲区的第一位开始存放)
         //dst
所指的缓冲区除了能放下num位之外至少还要出一个字节。
        void Get(size_t start, size_t num, unsigned char* dst)const{
              int bits = numeric_limits<unsigned char>::digits;
              //
第一个目标字节和该字节中的位偏移
              int src = start/bits;
              int off = start%bits;
              for(int i=0; i<(num+bits-1)/bits; ++i){
                     *(dst+i) = buf_[src+i] << off;
                     if(off > 0)
                            *(dst+i) |= buf_[src+i+1] >> (bits –off);//这个地方很巧
              }
        }
       private:

          vector<unsigned char> buf_;
          size_t size_;
          //
创建一个前 n 位为 1 ,剩余位为 0 的掩码。
        unsigned char FirstBits(size_t n){
              int num = min(n, numeric_limits<unsigned char>::digits;
              unsigned char b = 0;
              while(num-- > 0)
                     b = (b >> 1) | (1 << (numeric_limits<unsigned char>::digits-1));
              return b;
        }
};
b.class BitBuffer{
public:
        //
添加p所指的位缓冲区头部的num个位
        void Append(unsigned char* p, size_t num){
              int bits = numeric_limits<unsigned char>::digit;//
这是得到unsigned char的位数
              for(int i= 0; i<num; ++i){
                     buf_.push_back(*p & ( 1<< (bits-1-i%bits)));
                     if((i+1) % bits == 0)
                            ++p;
              }
        }
         //
查询正被使用的位数目(初始为0
        size_t Size() const{
                     return size_;
        }
          //
从第start位(位的编号从0开始)开始获取num个位,并将结果存入dst所指的缓冲区(从缓冲区的第一位开始存放)
          void Get(size_t start, size_t num, unsigned char* dst)const{
                  int bits = numeric_limits<unsigned char>::digits;
            *dst = 0;
                  for(int i = 0; i < num; ++i){
                         *dst |= unsigned char(buf_[start+i]) << (bits-1-i%bits);
                         if((i+1)%bits == 0)
                            *++dst = 0;
                     }
              }
       private:
              vector<bool> buf_;
};

    

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值