位运算(1)-- 基础

来源:互联网 发布:时序数据库应用场景 编辑:程序博客网 时间:2024/05/29 23:44

一、基础

与:a & b
或:a | b
非:~ a
异或:a ^ b
左移:a << b
右移:a >> b

二、基本技巧

与操作:

  • 指定位清零:void clr(int& a, int i) { a &= ~(1 << i); }
  • 获取指定位的值:int get(int a, int i) { return a & (1 << i); }
  • 保留某些位不变:操作数相应的要保留的位设置位1,如:a &= 0xff
  • 判断一个数是否是2的幂:bool isPow2(int a) { return !(a & (a - 1)) && a; }
  • 奇偶性判断:a & 1

或操作:

  • 设置指定位:void set(int& a, int i) { a |= (1 << i); }

取反操作:

  • 相反数:int opposite(int a) { return ~a + 1;} (原码和补码的相互转换)

异或操作:

  • 交换两个整数:void swap(int& a, int& b) { a ^= b ^= a ^= b; }
  • 指定位取反:void reverse(int& a, int i) { a ^= (1 << i); }

移位操作:

  • 2的幂次:a << = 2
  • 循环移位:void rol(int& a, int k) { a = a << k | a >> 32 - k; }

三、简单应用

  • average函数:计算两个整数xy的平均值,(x + y) / 2可能溢出,二分法常用的x + (y - x) / 2也可能溢出,使用位运算可避免该问题。
    实现int average = (x & y) + ((x ^ y) >> 1);
    原理:回想一下半加器的原理,半加器有两部分逻辑:本位 S=AB(异或) 和进位 C=AB (与)。将多位加法的本位和进位分开考虑,进位则为(x & y) << 1(进位的结果需要加到上一位),本位则为x ^ y,从而平均值为(x & y) + ((x ^ y) >> 1).

  • abs函数:位运算避免常规方法求绝对值的分支预测
    实现int abs(int x) { int y = x >> 31; return (x + y) ^ y; }
    原理-x = ~x + 1 = ~(x - 1). y = x >> 31是获得符号位。
    x >= 0时,y = 0, 此时(x + 0) ^ 0 = x;
    x < 0 时,y = -1或者(0xFFFFFFFF),此时(x + y) ^ y就变为(x - 1) ^ 0xFFFFFFFF(这一步异或相当于取反) = ~(x - 1) = -x.

  • max函数:同样避免分支预测。
    实现int max(int x, int y) { return x ^ ( (x ^ y) & (x - y) >> 31 ); }
    原理:利用异或的性质,首先获得x - y的符号位,为正符号为0否则为-1.当符号位为0时,x >= y,此时(x ^ y) & 0 = 0, x ^ 0 = x; 当符号位为-1时,(x ^ y) & 0xFFFFFFFF = x ^ y, x ^ (x ^ y) = y.

  • 常整数乘法:通过位运算对常整数的乘法进行优化。
    如:计算x = x * K
    将常整数K的二进制表示表达为一组 0 和 1 交替的序列:
    [(0…0)(1…1)(0…0)(1…1)…]
    考虑一组从位置 n 到 m 的连续 1 (n >= m),可以用以下两种形式将乘法变为位运算:
    A:(x << n) + (x << n - 1) + ... + (x << m )
    B:(x << n + 1) - (x << m)

0 0
原创粉丝点击