位运算

位运算看似一个很小,很不起眼的知识,很容易被很多人忽略。但它着实很重要,故在此做下总结。

1 位运算操作符

        &       与运算  
        |       或运算  
        ^       异或运算  
        ~       非运算(求补)  
      >>       右移运算  
      <<       左移运算  

2 各种运算符的使用规则

      * 与运算

  与运算(&)  
  双目运算。二个位都置位(等于1)时,结果等于1,其它的结果都等于0。  
        1       &       1       ==       1  
        1       &       0       ==       0  
        0       &       1       ==       0  
        0       &       0       ==       0  

     * 或运算

  或运算(   |   )  
  双目运算。二个位只要有一个位置位,结果就等于1。二个位都为0时,结果为0。  
        1       |       1       ==       1  
        1       |       0       ==       1  
        0       |       1       ==       1  
        0       |       0       ==       0   
    * 异或运算

  异或运算(^)  
  双目运算。二个位不相等时,结果为1,否则为0。  
   
        1       ^       1       ==       0  
        1       ^       0       ==       1  
        0       ^       1       ==       1  
        0       ^       0       ==       0  

   * 非运算

  非运算(~)  
  单目运算。位值取反,置0为1,或置1为0。非运算的用途是将指定位清0,其余位置1。非运算与数值大小无关

   * 移位运算

  移位运算(>>   与   <<)  
  将位值向一个方向移动指定的位数。右移   >>   算子从高位向低位移动,左移   <<   算子从低位向高位移动。往往用位移来对齐位的排列(如MAKEWPARAM,   HIWORD,   LOWORD   宏的功能)。

3 位运算的一些功能

 (1) 一个整数中包含多少二进制位1

   方法1:

Code:
  1. #include <iostream>   
  2. #include <string>   
  3. using namespace std;   
  4. int main()   
  5. {   
  6.      int count = 0;   
  7.     int m = 9999;   
  8.    while (m)   
  9.     {   
  10.         if (m&1 == 1)   
  11.         {   
  12.             count++;   
  13.         }   
  14.         m >>= 1;   
  15.     }   
  16.     cout << count << endl;   
  17.     return 0;   
  18. }  

   方法二:

Code:
  1. #include <iostream>   
  2. #include <string>   
  3. using namespace std;   
  4. int main()   
  5. {   
  6.     int count = 0;   
  7.     int m = 9999;   
  8.     while (m)   
  9.     {   
  10.         count++;   
  11.         m = m&(m-1);   
  12.     }   
  13.     cout << count;   
  14.     return 0;   
  15. }  

  (2)判断某一位是否为1

    直接让该位为1,其他为均为0 的数与要判断的数字相与,结果若是1,则该位即为1,否则为0.

  如:判断120的最后一位是否为1.

Code:
  1. #include <iostream>   
  2. #include <string>   
  3. using namespace std;   
  4. int main()   
  5. {   
  6.     int m = 120;   
  7.     if (m&1 == 1)   
  8.     {   
  9.         cout << "最后一位为1." << endl;   
  10.     }   
  11.     else  
  12.     {   
  13.         cout << "最后一位为0." << endl;   
  14.     }   
  15.     return 0;   
  16. }  

 这里判断的是二进制位。

   (3):判断整型变量是奇数还是偶数

由于我们知道整型变量偶数数字的个位数字只能为0,2,4,6,8

0 :  0000

2:  0010

4:  0100

6: 0110

8: 1000
我们看出一个规律,即他们的二进制最后一位均为0.故我们可以这样判断一个数的奇偶性。

Code:
  1. #include <iostream>   
  2. #include <string>   
  3. using namespace std;   
  4. int main()   
  5. {   
  6.     cout << "Please input a integer:" << endl;   
  7.     int a;   
  8.     cin >> a;   
  9.     if ((a&1) == 0)   
  10.     {   
  11.         cout << a << " is a even number." << endl;   
  12.     }   
  13.     else  
  14.     {   
  15.         cout << a << " is a odd number." << endl;   
  16.     }   
  17.     return 0;   
  18. }  

 (4):取int型变量a的第k位

这个问题用到了移位操作,至于向右移几位就看我们的最右边的移位是按0位还是按1位算了,若是按0,可以这样做:

Code:
  1. a>>k&1  

 (5): 将int型变量a的第k位清0

对于这个就隐含了两个要求了,即①不能改变其他位②将第k位清零

对于这个我们首先想到与运算,即用原数字和一个第k位为0,其他位均为1的数字相与,但是难点是我们怎么得到一个第k位为0,其他位为1的数字呢。这就要用到移位运算了:

Code:
  1. 1 << k;  

我们可以用1向左移位k位。最后结果为第k位为1,其他位均为0.正好和我们的要求相反。所以我们只需用取反操作即可:

Code:
  1. a & ~(1 << k);   

这个就是最后的表达式了。

(6):int型变量循环左移k次

 对于这个循环左移迷糊了。这道题的答案为:

Code:
  1. a=a < <k|a>>16-k  (设sizeof(int)=16)  

(7):整数的平均值

这里我们肯定首先想到了 (x+y)/2.但是这就有个问题了。因为x+y可能溢出。那我们该怎么算平均值呢。可以这样:

Code:
  1. int average(int x, int y)  //返回X,Y 的平均值    
  2. {       
  3.     return (x&y)+((x^y)>>1);    
  4. }   

(8)判断一个整数是不是2的幂,对于一个数 x >= 0,判断他是不是2的幂

  对于此题我们从这些数的二进制位上可以找到规律:

2: 0010

4: 0100

8: 1000

......

即他们只有一位为1,其它为均为0.

我们对他们进行减1操作:

1: 0001

3: 0011

7: 0111

对他们相与操作若结果为0,即可认为该数是2的幂。(大于0的数)。

Code:
  1. boolean power2(int x)    
  2. {    
  3.     return ((x&(x-1))==0)&&(x!=0);    
  4. }    

(9):不用temp交换两个整数

Code:
  1. void swap(int x , int y)    
  2. {    
  3.     x ^= y;    
  4.     y ^= x;    
  5.     x ^= y;    
  6. }   

这个对各个式子带入结合就和容易明白。这里就不在解释。

(10):计算绝对值

Code:
  1. int abs( int x )    
  2. {    
  3. int y ;    
  4. y = x >> 31 ;    
  5. return (x^y)-y ;        //or: (x+y)^y    
  6. }   

首相将x向右移位31位

(11):取相反数

这个就相对比较简单了。即取反+1即可 (~a + 1);

(12):a % 2 等价于 a & 1

这个很容易明白。对2取余结果不是0,就是1.仅和最后一位有关。

(13): if (x == a) x= b;
            else x= a; 
等价于 x= a ^ b ^ x;

这个是异或运算的问题。我们也可以写成这样 x ^= a ^ b;

这里有个规则:① 任意两个相等的数相异或结果为0,② 0与任何数相异或等于任何数。这个根据异或的性质很容易得出来。

(14):除法,乘法运算与移位运算

左移一位相当于乘2,右移一位相等于除2.

Code:
  1. #include <iostream>   
  2. #include <string>   
  3. using namespace std;   
  4. int main()   
  5. {   
  6.     int b = 5;   
  7.     b <<= 2;   
  8.     cout << b << endl;   
  9.     b >>= 1;   
  10.     cout << b << endl;   
  11.     b >>= 1;   
  12.     cout << b << endl;   
  13.     b >>= 1;   
  14.     cout << b << endl;   
  15.     return 0;   
  16. }  

一个简单的测试。

(15): 其他实例:

实例

    功能              |          示例            |    位运算
----------------------+---------------------------+--------------------
去掉最后一位          | (101101->10110)          | x >> 1
在最后加一个0        | (101101->1011010)        | x < < 1
在最后加一个1        | (101101->1011011)        | x < < 1+1
把最后一位变成1      | (101100->101101)          | x | 1
把最后一位变成0      | (101101->101100)          | x | 1-1
最后一位取反          | (101101->101100)          | x ^ 1
把右数第k位变成1      | (101001->101101,k=3)      | x | (1 < < (k-1))
把右数第k位变成0      | (101101->101001,k=3)      | x & ~ (1 < < (k-1))
右数第k位取反        | (101001->101101,k=3)      | x ^ (1 < < (k-1))
取末三位              | (1101101->101)            | x & 7
取末k位              | (1101101->1101,k=5)      | x & ((1 < < k)-1)

取右数第k位          | (1101101->1,k=4)          | x >> (k-1) & 1

把末k位变成1          | (101001->101111,k=4)      | x | (1 < < k-1)
末k位取反            | (101001->100110,k=4)      | x ^ (1 < < k-1)
把右边连续的1变成0    | (100101111->100100000)    | x & (x+1)
把右起第一个0变成1    | (100101111->100111111)    | x | (x+1)
把右边连续的0变成1    | (11011000->11011111)      | x | (x-1)
取右边连续的1        | (100101111->1111)        | (x ^ (x+1)) >> 1
去掉右起第一个1的左边 | (100101000->1000)        | x & (x ^ (x-1))
判断奇数       (x&1)==1
判断偶数 (x&1)==0 

参考帖子:http://topic.csdn.net/u/20080626/20/59a05c26-acb3-4d74-a153-711ce3a664ff.html

http://topic.csdn.net/t/20041019/00/3468250.html

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值