C++ 位运算

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;
    }
};

猜你喜欢

转载自blog.csdn.net/qjh5606/article/details/81698428