C++ 位运算
二进制数按位运算
符号 | 描述 | 运算规则 |
---|---|---|
& | 与 | 两位都为1,结果为1 |
| | 或 | 有一位为1,结果为1 |
~ | 非 | ~0=1;~1=0 |
^ | 异或 | 两位不同,结果为1 |
<< | 左移 | 各二进制全部左移若干位,高位丢弃,低位补0 |
>> |
右移 | 各二进制全部右移若干位.对于无符号数,高位补0;有符号数.各编译器处理方式不同.有时补0(逻辑右移),有时补符号位(算术右移). |
计算机中数字的存储方式
- 计算机中,对数字的表示有三种方式:
原码
反码
补码
- 原码:原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为0,负数该位为1。比如十进制3如果用8个二进制位来表示就是 00000011, -3就是 10000011。
- 反码:反码表示方法:正数的反码是其本身;负数的反码是在其原码的基础上,符号位不变,其余各个位取反。
- 补码:补码表示方法:正数的补码是其本身;负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1。 (即在反码的基础上+1)
- 在计算机系统中,数值一律用补码来表示、运算和存储。 使用补码,可以将符号位和数值域统一处理,将加法和减法统一处理。
经典应用 | 常用操作技巧
1 判断奇偶
- 最未位是0还是1来决定: 为0就是偶数,为1就是奇数
- if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数
2 交换两数
一般要引入临时变量
用位操作来实现交换两数而不用第三方变量
void Swap(int &a, int &b) { if (a != b) { a ^= b; b ^= a; a ^= b; } }
3 取反 ~
符号的应用
i+(~i) = -1
i各位取反. 再与i相加 . 结果为 -1.
求 n+1 或 n-1
-~n = n+1
: ~n各位取反, 符号-, 再对其取反再加1.~-n = n-1
: 找到最低位的第一个1.对其取反并把该为后的所有位也取反.取相反数
~n +1
(n^-1)+1
4 求绝对值
对于32位的int.
int my_abs(int a) { int i = a >> 31; return i == 0 ? a : (~a + 1); } // 优化 int my_abs(int a) { int i = a >> 31; return ((a ^ i) - i); }
5 求二进制中1的个数!!!
/* Version 1 */
int count_bits(int n)
{
return n ? (n & 1) + count_bits(n >> 1) : 0;
}
/* Version 2 */
int count_bits(int n)
{
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f);
x = (x & 0x00ff00ff) + ((x >> 8) & 0x00ff00ff);
x = (x & 0x0000ffff) + ((x >> 16) & 0x0000ffff);
return x;
}
关于版本2 整个程序是一个分治的思想。
第一次我们把每相邻的两位加起来,得到每两位里 1 的个数,比如前两位 10 就表示原数的前两位有 2 个 1。
第二次我们继续两两相加,10+01=11,00+10=10,得到的结果是 00110010,它表示原数前 4 位有 3 个 1,末 4 位有 2 个 1。
最后一次我们把 0011 和 0010 加起来,得到的就是整个二进制中 1 的个数。
//以十进制数 211 为例,其二进制为 11010011, | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | <— 原数 +---+---+---+---+---+---+---+---+ | 1 0 | 0 1 | 0 0 | 1 0 | <— 第一次运算后 +-------+-------+-------+-------+ | 0 0 1 1 | 0 0 1 0 | <— 第二次运算后 +---------------+---------------+ | 0 0 0 0 0 1 0 1 | <— 第三次运算后,得数为 5 +-------------------------------+
6 判断二进制中1的奇偶性
x = x ^ (x >> 1);
x = x ^ (x >> 2);
x = x ^ (x >> 4);
x = x ^ (x >> 8);
x = x ^ (x >> 16);
cout << (x & 1) << endl; // 输出 1 为奇数
7 判断一个数是否2的幂
2的幂 即二进制表示下只有一个1.
如果是 2 的幂,
n - 1
就是把 n 的二进制的最低的那个 1 取反为 0,并把后面的 0 全部取反为 1。bool is_power_of_two(int n) { return (n > 0) ? (n & (n - 1)) == 0 : false; }
8 变换符号
对一个数 取反再加1即可.
int SignReversal(int a) { return ~a + 1; }
9 高低位互换
- 给出一个16位的无符号整数。称这个二进制数的前8位为“高位”,后8位为“低位”。 现在写一程序将它的高低位交换。
a = (a >> 8) | (a << 8);
10 二进制逆序
4步得到16位数据的二进制逆序
- 第一步:每2位为一组,组内高低位交换
- 第二步:每4位为一组,组内高低位交换
- 第三步:每8位为一组,组内高低位交换
- 第四步:每16位为一组,组内高低位交换
a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1);
a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2);
a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4);
a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8);
- 对于32位的话:
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
uint32_t a = n;
a = ((a & 0xAAAAAAAA) >> 1) | ((a & 0x55555555) << 1);
a = ((a & 0xCCCCCCCC) >> 2) | ((a & 0x33333333) << 2);
a = ((a & 0xF0F0F0F0) >> 4) | ((a & 0x0F0F0F0F) << 4);
a = ((a & 0xFF00FF00) >> 8) | ((a & 0x00FF00FF) << 8);
a = ((a & 0xFFFFF0000) >> 16) | ((a & 0x0000FFFF) << 16);
return a;
}
};