位运算求余仅适用于除数为2的整数次幂的场景,此时可用a&(b−1)替代a%b,原理是提取二进制数低n位,高效且符合余数定义。

位运算求余不是通用替代方案,而是针对特定场景的高效技巧:仅当除数是 2 的整数次幂(如 2、4、8、16、32…)时,才能用位运算安全、准确地代替 %。它本质是利用二进制结构直接提取余数位,跳过除法过程,因此速度快、指令少,特别适合嵌入式、高频循环或性能敏感代码。
为什么只能用于 2 的幂次方?
因为计算机中整数以二进制存储,而除以 2n 等价于逻辑右移 n 位——高位部分构成商,被移出的低 n 位恰好就是余数。例如:
- 13 的二进制是 1101,除以 8(即 2³),右移 3 位得商 1(1),移出的低 3 位 101 就是余数 5;
- 要“抓取”这低 3 位,只需和 111(即 2³ − 1 = 7)做按位与:
1101 & 0111 = 0101→ 5。
两种等效写法及适用场景
设待求余数为 x % y,且 y == 2ⁿ,则可任选其一:
-
x & (y - 1)—— 最常用,简洁直观。如97 & 7求除以 8 的余数(7 是 0b111); x - ((x >> n) —— 先右移得商,再左移还原高位部分,用原值减去即得余数。适合需要同时获取商与余数的场合,且对负数行为更可控(取决于右移类型)。
实战注意事项
直接套公式容易踩坑,关键细节必须明确:
-
只适用于非负数或已知补码行为的环境:C/C++ 中对负数右移是实现定义的(通常算术右移),
x & (y-1)在负数下可能返回非预期结果(如 -1 & 7 = 7,但数学余数应为 7?实际需看语言规范)。Java 中>>>是无符号右移,配合&更稳妥; -
确保 y 确实是 2 的幂:可用
(y & (y-1)) == 0 && y != 0快速判断(前提是 y > 0); -
编译器常自动优化 % 2ⁿ:现代编译器(GCC/Clang/MSVC)在开启优化后,会将
x % 8自动转为x & 7,手写位运算未必提升性能,但能显式表达意图、避免魔数。
从余数反推二进制理解
每次用 & (2ⁿ−1),你其实是在做一件很底层的事:屏蔽高位,只保留最低 n 位。这正是余数的定义——无法被 2ⁿ 整除的部分,必然落在低 n 位上。多试几个数:
-
0 & 3 → 0(0b00) -
1 & 3 → 1(0b01) -
2 & 3 → 2(0b10) -
3 & 3 → 3(0b11) -
4 & 3 → 0(0b100 → 取低两位 00)
你会发现结果严格循环于 0 到 3,这就是模 4 的完整余数空间。掌握这点,你就真正读懂了“余数即低位截断”这一二进制本质。

















