About Bit Manipulation (Bit-Twiddling)

位运算

常用 trick

  • 学会用 bitset
  • x & 1 检测 x 是否为奇
  • x & (x-1) 可以检测是否是2的整数次幂 (x&x-1 可以清除最右边的1,如果这个1是第一位的1, 那这个数就变为0)
    • x & 0xaaaaaaaa 判断 1 是不是都在偶数上,结合 x & x - 1 可以判断 4 的幂
    • x & 0x55555555 > 0 也可以判断 1 是不是在偶数,这个可以自己设计,可以变动的
    • 在golang里 貌似 x - 1 不加括号结果是错的
  • (x >> i) & 1 取出 x 的第 i 位二进制

基础知识

Binary,Octal和 Hexadecimal的表示 (通用)

  • Binary: 0b
  • Octal: 0 or 0o (Python和JS允许0o)
  • Hexadecimal: 0x or 0X

运算符,运算符顺序

运算符 描述 实例
& 按位与操作,按二进制位进行”与”运算。 运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1; (A & B) 将得到 12,即为 0000 1100
| 按位或运算符,按二进制位进行”或”运算。 运算规则:`0 0=0; 0
^ 异或运算符,按二进制位进行”异或”运算。 运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0; (A ^ B) 将得到 49,即为 0011 0001
~ 取反运算符,按二进制位进行”取反”运算。 运算规则:~1=-2; ~0=1; (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。
<< 二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0) A << 2 将得到 240,即为 1111 0000
>> 二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。 A >> 2 将得到 15,即为 0000 1111

位运算的赋值运算符

<<= 左移且赋值运算符 C <<= 2 等同于 C = C << 2
>>= 右移且赋值运算符 C >>= 2 等同于 C = C >> 2
&= 按位与且赋值运算符 C &= 2 等同于 C = C & 2
^= 按位异或且赋值运算符 C ^= 2 等同于 C = C ^ 2
|= 按位或且赋值运算符 C |= 2 等同于 C = C | 2

其他运算符

运算符 描述
sizeof sizeof 运算符返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。
Condition ? X : Y 条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。
, 逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。
.(点)和 ->(箭头) 成员运算符用于引用类、结构和共用体的成员。
Cast 强制转换运算符把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。
& 指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。
* 指针运算符 * 指向一个变量。例如,*var; 将指向变量 var。

运算符优先级 「由高到低」

类别 运算符 结合性
后缀 () [] -> . ++ - - 从左到右
一元 + - ! ~ ++ - - (type)* & sizeof 从右到左
乘除 * / % 从左到右
加减 + - 从左到右
移位 << >> 从左到右
关系 < <= > >= 从左到右
相等 == != 从左到右
位与 AND & 从左到右
位异或 XOR ^ 从左到右
位或 OR | 从左到右
逻辑与 AND && 从左到右
逻辑或 OR || 从左到右
条件 ?: 从右到左
赋值 = += -= *= /= %=>>= <<= &= ^= |= 从右到左
逗号 , 从左到右
1
2
3
4
5
6
0xaaaaaaaa = 10101010101010101010101010101010 (偶数位为1,奇数位为0) (4的幂)
0x55555555 = 1010101010101010101010101010101 (偶数位为0,奇数位为1
0x33333333 = 110011001100110011001100110011 (10每隔两位交替出现)
0xcccccccc = 11001100110011001100110011001100 (01每隔两位交替出现)
0x0f0f0f0f = 00001111000011110000111100001111 (10每隔四位交替出现)
0xf0f0f0f0 = 11110000111100001111000011110000 (01每隔四位交替出现)

190. 颠倒二进制位 「>>= 和 >> 的区别」

1
2
3
4
5
6
7
8
9
// res 一开始为0, 每次我们都将 res左移, 将n右移
uint32_t reverseBits(uint32_t n) {
uint32_t res = 0;
for (int i = 0; i < 32; ++i) {
res = (res << 1) | (n & 1);
n >>= 1;
}
return res;
}

XOR 的性质:

  1. x⊕x=0 , x⊕0 = x

  2. x⊕y=y⊕x (交换律)

  3. (x⊕y)⊕z=x⊕(y⊕z) (结合律) 「不支持分配律」

  4. x⊕y⊕y=x (自反性)

  5. ∀i∈Z,有 4i⊕(4i+1)⊕(4i+2)⊕(4i+3)=0


如何取按照要求 取一个整型中的某些二进制位?

  • 对于x , 取其第i位的二进制 $\rightarrow$ (x >> i) & 1 (从0开始)

  • 对于x,清除第 i 位的二进制

  • 如果我们框定了x的范围,比如我们确认x <= 1024 那么32位中必然只需要使用其中最右边的10位

    • 想取 高4位(即10位中左边的4位) 那显然 res = x >> 6
    • 想取 低6位 则不需要移位,可以通过==与运算(和全1与)==直接取 比如这里 2^6 -1 res = x & 63;

关于进制转换问题

  • 我们使用的进制转换是短除法的进制转换,因此最后有一个 reverse 操作,不要忘记

交换x y

  • 直接交换法: a = a - b , b = a + b, a = b - a;
  • 异或法 : x = x ^ y, y = x ^ y, x = x ^ y

__builtin_popcount(i) 计算32位整型里 1的个数

  • 一句话逻辑运算
    • or => 只要有 1 结果为 1
    • and => 只有 1 & 1 = 1,其他为 0
      • 取奇偶(配合移位取二进制)
      • 分组(见找出 2 个只出现 1 次的数, 核心思想:按照某一位是否为 1 来把数组分成两个部分)
    • xor => 两个二进制不同, 结果为 1 / 二进制不进位加法
      • 抵消 / 二进制交换

About Bit Manipulation (Bit-Twiddling)
https://www.mementos.top/1976/04/01/about Bit Manipulation/
作者
Natsumi
发布于
1976年4月1日
许可协议