rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • 位运算

  • 基础概念
  • 常用位运算及规则
    • 1. 按位与(AND):&
    • 2. 按位或(OR):|
    • 3. 按位异或(XOR):^
    • 4. 按位取反(NOT):~
    • 5. 左移(Shift Left):<<
    • 6. 右移(Shift Right):>>(算术右移)与 >>>(逻辑右移)
  • 复合位运算(赋值操作)
  • 典型应用场景
  • 注意事项
  • 位运算常用操作
    • 基本
    • 判断奇偶
    • a、b两个数交换
    • 变换符号,正变负,负变正
    • 求绝对值
    • 判断一个数num是不是 2 的幂
    • 判断一个数num是不是 4 的幂
    • int 型最大值是什么?
    • int 型最小值是什么?
    • long 型最大值是什么?
    • 整数n乘以2是多少?
    • 整数n除以2是多少?(负奇数的运算不可用)
    • 乘以2的n次方,例如计算10 * 8(8是2的3次方)
    • 除以2的n次方,例如计算16 / 8(8是2的3次方)
    • 取两个数的最大值(某些机器上,效率比a>b ? a:b高)
    • 取两个数的最小值(某些机器上,效率比a>b ? b:a高)
    • 判断符号是否相同(true 表示 x和y有相同的符号, false表示x,y有相反的符号。)
    • 计算2的n次方 n > 0
    • 求两个整数的平均值
    • 从低位到高位,取n的第m位
    • 从低位到高位.将n的第m位置为1,将1左移m-1位找到第m位,
    • 从低位到高位,将n的第m位置为0,将1左移m-1位找到第m位,
    • 清零
    • & 取一个数的指定位
    • | 常用来对一个数据的某些位设置为1
    • ^ 翻转指定位
    • ~ 使一个数的最低位为零
  • 资料

位运算

进制

基础概念

位运算(Bitwise Operation)是直接对整数的二进制位进行操作的运算方式,由于操作底层二进制,执行效率极高,广泛用于系统编程、算法优化、数据压缩等场景。以下是位运算的核心概念、常用操作及应用:

  • 二进制位:计算机中数据以二进制(0 和 1)存储,每个二进制数字称为 “位(bit)”。
  • 操作对象:位运算的操作数是整数(正整数、负整数、0),运算时先转换为二进制补码形式(计算机存储整数的标准形式)。
  • 运算特点:按位操作,即两个操作数的对应二进制位分别进行运算,结果组成新的二进制数。

常用位运算及规则

& 与运算 遇0则0
| 或运算 遇1则1
^ 异或 不进位加 相同为0 相异为1
~ 取反 1-0 0-1
>> 右移(逻辑位移) 补符号位 符号为1补1 符号为0补0
<< 左移(算数位移) 永远补0
>>> 无符号右移  永远补0

Kotlin 里的七种位运算符

and(bits) – 位与 (Java 的 &)
or(bits) – 位或 (Java 的 |)
inv() – 位非 (Java 的 ~)
xor(bits) – 位异或 (Java 的 ^)
shl(bits) – 有符号左移 (Java 的 <<)
shr(bits) – 有符号右移 (Java 的 >>)
ushr(bits) – 无符号右移 (Java 的 >>>)
符号含义详解
&位与两个比特位都为 1 时,结果才为 1,否则为 0 (位与操作满足交换律和结合律,甚至分配律)
|位或两个比特位都为 0 时,结果才为 0,否则为 1 (位或操作满足交换律和结合律,甚至分配律)
~位非即按位取反,1 变 0,0 变 1
^异或两个比特位相同时(都为 0 或都为 1)为 0,相异为 1(异或操作满足交换律和结合律,甚至分配律。任何整数和自己异或的结果为 0,任何整数与 0 异或值不变)
<<左移将所有的二进制位按值向←左移动若干位,左移操作与正负数无关,它只是傻傻地将所有位按值向左移动,高位舍弃,低位补 0
>>右移将所有的二进制位按值向右→移动若干位,低位直接舍弃,跟正负无关,而高位补 0 还是补 1 则跟被操作整数的正负相关,正数补 0 ,负数补 1
>>>无符号右移将所有的二进制位按值向右→移动若干位,低位直接舍弃,跟正负无关,高位补 0 ,也跟正负无关

1. 按位与(AND):&

  • 规则:两个对应位都为 1 时,结果位为 1;否则为 0。 (1 & 1 = 1,1 & 0 = 0,0 & 1 = 0,0 & 0 = 0)
  • 示例: 5 & 3 → 二进制 101 & 011 = 001 → 结果为 1。
  • 用途:
    • 提取指定位(如x & 0b111提取 x 的低 3 位)。
    • 判断奇偶(x & 1 == 1 为奇数,==0 为偶数)。

2. 按位或(OR):|

  • 规则:两个对应位有一个为 1 时,结果位为 1;否则为 0。 (1 | 1 = 1,1 | 0 = 1,0 | 1 = 1,0 | 0 = 0)
  • 示例: 5 | 3 → 二进制 101 | 011 = 111 → 结果为 7。
  • 用途:
    • 设置指定位为 1(如x | 0b100将 x 的第 3 位设为 1)。
    • 合并标志位(如权限控制中合并多个权限)。

3. 按位异或(XOR):^

  • 规则:两个对应位不同时,结果位为 1;相同则为 0。 (1 ^ 1 = 0,1 ^ 0 = 1,0 ^ 1 = 1,0 ^ 0 = 0)
  • 示例: 5 ^ 3 → 二进制 101 ^ 011 = 110 → 结果为 6。
  • 特性:
    • 自身异或为 0(x ^ x = 0)。
    • 与 0 异或不变(x ^ 0 = x)。
    • 交换律和结合律(a ^ b ^ c = a ^ (b ^ c))。
  • 用途:
    • 交换两个数(a = a ^ b; b = a ^ b; a = a ^ b,无需临时变量)。
    • 查找数组中唯一出现奇数次的数(异或所有元素,结果即为目标)。

4. 按位取反(NOT):~

  • 规则:将二进制位 0 变 1,1 变 0(包括符号位)。

  • 示例: 对于 32 位整数,~5 → 二进制 ~000...000101 = 111...111010 → 结果为 -6(补码规则)。

  • 用途:

    • 生成掩码(如~0 << n 生成高(32-n)位为 1 的掩码)。
    • 计算相反数(~x + 1 = -x,补码中负数是正数取反加 1)。

5. 左移(Shift Left):<<

  • 规则:将二进制位整体左移 n 位,高位溢出丢弃,低位补 0。 (相当于 x * 2ⁿ,前提是不溢出)
  • 示例: 5 << 2 → 二进制 101 << 2 = 10100 → 结果为 20(5×2²=20)。
  • 用途:快速计算乘以 2 的幂(比乘法运算高效)。

6. 右移(Shift Right):>>(算术右移)与 >>>(逻辑右移)

  • 算术右移(>>): 二进制位整体右移 n 位,低位溢出丢弃,高位补符号位(正数补 0,负数补 1)。 (相当于 x ÷ 2ⁿ,向下取整) 示例:-8 >> 2 → 二进制 11111000 >> 2 = 11111110 → 结果为 -2。
  • 逻辑右移(>>>): 二进制位整体右移 n 位,低位溢出丢弃,高位补 0(不考虑符号位,仅用于无符号数)。 示例:-8 >>> 2(32 位)→ 结果为 1073741822(高位补 0 后的值)。
  • 用途:快速计算除以 2 的幂、提取高位等。

复合位运算(赋值操作)

位运算可与赋值结合,简化代码:

  • x &= y → 等价于 x = x & y(按位与后赋值)
  • x |= y → 等价于 x = x | y(按位或后赋值)
  • x ^= y → 等价于 x = x ^ y(按位异或后赋值)
  • x <<= n → 等价于 x = x << n(左移后赋值)
  • x >>= n → 等价于 x = x >> n(右移后赋值)

典型应用场景

  1. 权限控制:用二进制位表示不同权限(如0b101表示权限 1 和 3),通过|合并权限、&检查权限。 例:hasPermission = (userPerm & targetPerm) == targetPerm。
  2. 算法优化:
    • 求两数平均值:(a + b) >> 1(避免溢出可用 a + ((b - a) >> 1))。
    • 清除最低位的 1:x & (x - 1)(如6 (110) & 5 (101) = 4 (100))。
    • 判断 2 的幂:x & (x - 1) == 0(2 的幂的二进制只有一个 1)。
  3. 数据压缩与加密:位运算常用于哈希算法(如 MD5)、对称加密(如 AES)中的底层变换,高效处理二进制数据。
  4. 硬件编程:嵌入式系统中,通过位运算操作寄存器的特定位(如控制 GPIO 引脚高低电平)。

注意事项

  • 符号位处理:负数以补码存储,位运算会操作符号位(如~会翻转符号位),需注意结果的正负。
  • 溢出问题:左移可能导致整数溢出(超出类型表示范围),结果可能不符合预期(如 Java 中1 << 31 是 -2147483648)。
  • 可读性权衡:位运算效率高,但过度使用会降低代码可读性,需在性能与可读性间平衡。

位运算的核心价值在于直接操作底层二进制,这使得它在需要高性能或直接与硬件交互的场景中不可替代。掌握位运算规则和常见技巧,能显著提升代码效率和解决复杂问题的能力。

位运算常用操作

基本

a & a = a
a | a = a
a ^ a = 0

a & ~a == 0;
a | ~a == -1;   
a ^ ~a == -1;

a & 0 = 0
a | 0 = a
a ^ 0 = a

a & -1 = a
a | -1 = -1
a ^ -1 = -(a+1)

a | ( a & b ) = a
a & ( a | b ) = a

// 当且仅当 a 是偶数(即 a 能被 2 整除,数学表示为 (a = $2k$, k \in \mathbb{Z}\))时,等式成立
a | 1 = a + 1

// 如果a是奇数,(a + 1)如果a是偶数
a ^ 1 = (a - 1)

// 删除整数 a 二进制最后一个 1 
// a-1 会翻转 a 最后一个 1 及右侧所有位,与 a 按位与后仅保留左侧不变的位
a & (a-1)

### 统计二进制中 1 的个数(汉明重量)
原理:每执行一次 a & (a - 1),就会删除一个 1,直到 a 变为 0(所有 1 都被删除),执行次数即为 1 的个数。
示例(统计 a=13 二进制中 1 的个数,13=1101,共 3 个 1):
1. a = 13 (1101) → a & (a-1) = 12 (1100) → 次数1;
2. a = 12 (1100) → a & (a-1) = 8 (1000)  → 次数2;
3. a = 8 (1000)  → a & (a-1) = 0 (0000)  → 次数3;
a=0,停止,结果为3。

// 判断一个数是否为 2 的幂
原理:2 的幂(如 2、4、8、16)的二进制有且只有一个 1(如 8=1000)。若 a 是 2 的幂,则 a & (a - 1) = 0(唯一的 1 被删除后变为 0);反之则不为 0。
示例:
a=8(2³):8 & 7 = 0 → 是 2 的幂;
a=9(1001):9 & 8 = 8 ≠ 0 → 不是 2 的幂。
2.2
a & (-a) = a(保留自身)
示例:8 & (-8) = 8 → 是 2 的幂;6 & (-6) = 2 ≠ 6 → 不是 2 的幂。


// 清除二进制末尾的连续 0
结合其他操作,可先通过 a & -a 找到最后一个 1 的位置(如 a=12=1100,a&-a=4=100),再用 a & (a - 1) 删除该 1,间接清除末尾的连续 0。

// 负数
-a = ~a + 1

// 仅保留最后一个1;
a & (-a)

a & -a 或者 a & (~a + 1) 获取最右侧1表示的值
比如: 
4 二进制是 100  4 & -4 等于0 4最右侧的1后面的值就是4的本身100   
5 二进制是 101  5 & -5 等于1 5最右侧的1后面的值就是1, 如果 5 - (5 & -5) 就相当于直接去掉最右侧的1  
6 二进制是 110  6 & -6 等于2 6最右侧的1后面的值就是2, 如果 6 - (6 & -6) 就相当于直接去掉最右侧的1  

// 比较两值是否相等
a ^ b == 0

// i+1位 置1
a |= 1<<i

// i+1位 置0
a &=~(1<<i)

// 取出i+1位(联系第5点)
a & (1<<i)

// 在对应i+1位,插入b的对应位;
a |=1<<i; (a的bit位置1)
a & (b & 1<<i) (与b的bit位相与)

// 得到全1
~0

// 保留最后i-1位;
a & ((1<<i)-1)

// 清零最后i-1位;
a & ~((1<<i)-1)

// 判断最高位是否为1
a<0

// 得到最高位的1;
a = a |(a>>1);
a = a |(a>>2);
a = a |(a>>4);
a = a |(a>>8);
a = a |(a>>16);
return (a+1)>>1;

判断奇偶

可以很简单归纳出,只需判断一个正整数的二进制补码最后一位是0是1,是0为偶数,是1为奇数

a & 1 = 0 或 1  a为奇数返回1、a为偶数返回0

// 判断奇偶(取出最后一位)
a & 1 等价于 a % 2(结果等于,位运算效率高)

public void isOddOrEven(int n){
    if ((n & 1) == 1) { //n是奇数
        System.out.println("Odd");
    }else { //n是偶数
        System.out.println("Even");
    }
}

a、b两个数交换

a = a ^ b
b = a ^ b
a = a ^ b

方式一:通过临时变了存放交换 效率最高

int temp = a;
a = b;
b = temp;

方式二:通过加减计算交换
a = a + b;
b = a - b;
a = a - b;

方式三:通过异或运算交换

a = a ^ b;
b = a ^ b;
a = a ^ b;

a ^= b;
b ^= a;
a ^= b;

方式四:非常不推荐,代码的简洁性是很重要的,但是也许面试题会考到

a = (a + b) - (b = a);
//用到的知识点:
//赋值运算符在连续赋值的时候会使用初始值代替中间变量

方式四:不推荐,理由同上
a = (a ^ b) ^ (b = a);

首先需要知道关于异或运算的一些性质:

异或满足交换律和结合律
x ^ x == 0
x ^ 0 == x

方式一是最常见的,可读性高,但是需要在内存中存放临时变量,但是对于现在来说,需要的内存空间很小,而且存放临时变量的内存很快就会释放,不存在问题。

方式二有缺陷,当两个数相加之后,可能其结果超出了变量类型能表达的最大范围,这个时候结果就会出问题,不推荐使用。

方式三效率是最高的,但是可读性不是很好。

在程序中我们应该尽可能的使用方式一,提高程序的可读性。但是需要了解方式三,以及方式三的原理。

变换符号,正变负,负变正

只需对待操作数应用取反操作后再加 1 即可

public void negate(){
    int a = -10, b = 10;
    System.out.println(~a + 1);//10
    System.out.println(~b + 1);//-10
}

求绝对值

对于负数可以通过上面变换符号的操作得到绝对值, 正数直接返回即可,因此我们要先判断符号位来得知当前数的正负。

public int abs(int a){
    int i = a >> 31;//得到符号位,0 为正数,-1 为负数
    return i == 0 ? a : (~a + 1);//符号位为 0 直接返回,否则返回 ~a + 1
}

或者,n>>31 取得n的符号,若n为正数,n>>31等于0,若n为负数,n>>31等于-1

若n为正数 n^0-0 数不变,若n为负数n^-1 需要计算n和-1的补码,异或后再取补码,

结果n变号并且绝对值减1,再减去-1就是绝对值

public int abs(int a){
    return (a ^ (a >> 31)) - (a >> 31);
}

判断一个数num是不是 2 的幂

如果是2的幂,n一定是100... (也就是二进制位里只有一个是1,且是首位,其余全是0), n-1就是011.... 所以做与运算结果为 0

判断一个数num是不是 4 的幂

 n > 0 && n & (n-1) == 0 && n % 3 == 1 

int 型最大值是什么?

System.out.println((1 << 31) - 1);// 2147483647, 注意运算符优先级,括号不可省略
System.out.println(~(1 << 31));// 2147483647

int 型最小值是什么?

System.out.println(1 << 31);
System.out.println(1 << -1);

long 型最大值是什么?

System.out.println(((long)1 << 127) - 1);

整数n乘以2是多少?

n << 1;

整数n除以2是多少?(负奇数的运算不可用)

n >> 1;

乘以2的n次方,例如计算10 * 8(8是2的3次方)

System.out.println(10<<3);

除以2的n次方,例如计算16 / 8(8是2的3次方)

System.out.println(16>>3);

取两个数的最大值(某些机器上,效率比a>b ? a:b高)

System.out.println(b&((a-b)>>31) | a&(~(a-b)>>31));

取两个数的最小值(某些机器上,效率比a>b ? b:a高)

System.out.println(a&((a-b)>>31) | b&(~(a-b)>>31));

判断符号是否相同(true 表示 x和y有相同的符号, false表示x,y有相反的符号。)

System.out.println((a ^ b) > 0);

计算2的n次方 n > 0

System.out.println(2<<(n-1));

求两个整数的平均值

System.out.println((a+b) >> 1);

从低位到高位,取n的第m位

int m = 2;
System.out.println((n >> (m-1)) & 1);

从低位到高位.将n的第m位置为1,将1左移m-1位找到第m位,

//得000...1...000,n 再和这个数做或运算
System.out.println(n | (1<<(m-1)));

从低位到高位,将n的第m位置为0,将1左移m-1位找到第m位,

//取反后变成111...0...1111,n再和这个数做与运算
System.out.println(n & ~(0<<(m-1)));

清零

如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。

& 取一个数的指定位

比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。

| 常用来对一个数据的某些位设置为1

比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。

^ 翻转指定位

比如将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X^Y=1010 0001)即可得到。

~ 使一个数的最低位为零

使a的最低位为0,可以表示为:a & ~1。~1的值为 1111 1111 1111 1110,再按"与"运算,最低位一定为0。因为“ ~”运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。

资料

bithacks

位运算有什么奇技淫巧?

Java 位运算超全面总结

位掩码的介绍与使用

交换变量效率

https://wdxtub.com/interview/14520595848890.html

https://juejin.im/post/5dc2cc0b6fb9a04a916d0ba0

最近更新:: 2025/9/27 00:43
Contributors: luokaiwen