位运算看似一个很小,很不起眼的知识,很容易被很多人忽略。但它着实很重要,故在此做下总结。
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:
- #include <iostream>
- #include <string>
- using namespace std;
- int main()
- {
- int count = 0;
- int m = 9999;
- while (m)
- {
- if (m&1 == 1)
- {
- count++;
- }
- m >>= 1;
- }
- cout << count << endl;
- return 0;
- }
方法二:
- #include <iostream>
- #include <string>
- using namespace std;
- int main()
- {
- int count = 0;
- int m = 9999;
- while (m)
- {
- count++;
- m = m&(m-1);
- }
- cout << count;
- return 0;
- }
(2)判断某一位是否为1
直接让该位为1,其他为均为0 的数与要判断的数字相与,结果若是1,则该位即为1,否则为0.
如:判断120的最后一位是否为1.
- #include <iostream>
- #include <string>
- using namespace std;
- int main()
- {
- int m = 120;
- if (m&1 == 1)
- {
- cout << "最后一位为1." << endl;
- }
- else
- {
- cout << "最后一位为0." << endl;
- }
- return 0;
- }
这里判断的是二进制位。
(3):判断整型变量是奇数还是偶数
由于我们知道整型变量偶数数字的个位数字只能为0,2,4,6,8
0 : 0000
2: 0010
4: 0100
6: 0110
8: 1000
我们看出一个规律,即他们的二进制最后一位均为0.故我们可以这样判断一个数的奇偶性。
- #include <iostream>
- #include <string>
- using namespace std;
- int main()
- {
- cout << "Please input a integer:" << endl;
- int a;
- cin >> a;
- if ((a&1) == 0)
- {
- cout << a << " is a even number." << endl;
- }
- else
- {
- cout << a << " is a odd number." << endl;
- }
- return 0;
- }
(4):取int型变量a的第k位
这个问题用到了移位操作,至于向右移几位就看我们的最右边的移位是按0位还是按1位算了,若是按0,可以这样做:
- a>>k&1
(5): 将int型变量a的第k位清0
对于这个就隐含了两个要求了,即①不能改变其他位②将第k位清零
对于这个我们首先想到与运算,即用原数字和一个第k位为0,其他位均为1的数字相与,但是难点是我们怎么得到一个第k位为0,其他位为1的数字呢。这就要用到移位运算了:
- 1 << k;
我们可以用1向左移位k位。最后结果为第k位为1,其他位均为0.正好和我们的要求相反。所以我们只需用取反操作即可:
- a & ~(1 << k);
这个就是最后的表达式了。
(6):int型变量循环左移k次
对于这个循环左移迷糊了。这道题的答案为:
- a=a < <k|a>>16-k (设sizeof(int)=16)
(7):整数的平均值
这里我们肯定首先想到了 (x+y)/2.但是这就有个问题了。因为x+y可能溢出。那我们该怎么算平均值呢。可以这样:
- int average(int x, int y) //返回X,Y 的平均值
- {
- return (x&y)+((x^y)>>1);
- }
(8)判断一个整数是不是2的幂,对于一个数 x >= 0,判断他是不是2的幂
对于此题我们从这些数的二进制位上可以找到规律:
2: 0010
4: 0100
8: 1000
......
即他们只有一位为1,其它为均为0.
我们对他们进行减1操作:
1: 0001
3: 0011
7: 0111
对他们相与操作若结果为0,即可认为该数是2的幂。(大于0的数)。
- boolean power2(int x)
- {
- return ((x&(x-1))==0)&&(x!=0);
- }
(9):不用temp交换两个整数
- void swap(int x , int y)
- {
- x ^= y;
- y ^= x;
- x ^= y;
- }
这个对各个式子带入结合就和容易明白。这里就不在解释。
(10):计算绝对值
- int abs( int x )
- {
- int y ;
- y = x >> 31 ;
- return (x^y)-y ; //or: (x+y)^y
- }
首相将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.
- #include <iostream>
- #include <string>
- using namespace std;
- int main()
- {
- int b = 5;
- b <<= 2;
- cout << b << endl;
- b >>= 1;
- cout << b << endl;
- b >>= 1;
- cout << b << endl;
- b >>= 1;
- cout << b << endl;
- return 0;
- }
一个简单的测试。
(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.youkuaiyun.com/u/20080626/20/59a05c26-acb3-4d74-a153-711ce3a664ff.html