CCM

Back

位运算技巧整理Blur image

1. 计算整数的符号#

/**
* ### 1. 计算整数的符号 ###
* 计算整数的符号。
* @param v 待计算的整数。
* @return -1 (负数), 0 (零), 或 +1 (正数)。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign">Original C version</a>
*/
public static int getSign(int v) {
return (v >> 31) | (-v >>> 31);
}

2. 检测两个整数的符号是否相反#

/**
* ### 2. 检测两个整数的符号是否相反 ###
* @param x 第一个整数。
* @param y 第二个整数。
* @return 如果符号相反则返回 true。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#DetectOppositeSigns">Original C version</a>
*/
public static boolean haveOppositeSigns(int x, int y) {
return (x ^ y) < 0;
}

3. 无分支计算整数绝对值#

/**
* ### 3. 无分支计算整数绝对值 ###
* @param v 待计算的整数。
* @return v 的绝对值。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#IntegerAbs">Original C version</a>
*/
public static int absWithoutBranch(int v) {
int mask = v >> 31;
return (v + mask) ^ mask;
}

4. 无分支计算最小值和最大值#

为了代码的清晰性和可维护性,优先使用 Math.min() / Math.max() 或者简单的三元运算符,它们的性能在现代JVM上已经优化得非常好了。

/**
* ### 4. 无分支计算两个整数的最小值 ###
* @param x 第一个整数。
* @param y 第二个整数。
* @return x 和 y 中的较小值。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax">Original C version</a>
*/
public static int minWithoutBranch(int x, int y) {
return y ^ ((x ^ y) & ((x - y) >> 31));
}
public static int maxWithoutBranch(int x, int y) {
return x ^ ((x ^ y) & ((x - y) >> 31));
}

5. 判断一个整数是否是2的幂#

/**
* ### 5. 判断一个整数是否是2的幂 ###
* @param v 待检查的正整数。
* @return 如果是2的幂则返回 true。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2">Original C version</a>
*/
public static boolean isPowerOfTwo(int v) {
return (v > 0) && ((v & (v - 1)) == 0);
}

6. 符号扩展#

/**
* ### 6. 符号扩展 ###
* 从可变位宽进行符号扩展。
* @param x 包含b位有符号数的整数。
* @param b x中实际使用的位数 (1-32)。
* @return 符号扩展后的整数。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend">Original C version</a>
*/
public static int signExtendFrom(int x, int b) {
if (b <= 0 || b > 32)
throw new IllegalArgumentException("Bit width b must be between 1 and 32.");
if (b == 32) return x;
int mask = 1 << (b - 1);
int value = x & ((1 << b) - 1);
return (value ^ mask) - mask;
}

7. 无分支地条件性设置或清除位#

/**
* ### 9. 无分支地条件性设置或清除位 ###
* 等效于: if (flag) w |= m; else w &= ~m;
* @param w 待修改的字。
* @param m 掩码。
* @param flag 条件标志。
* @return 修改后的字。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching">Original C version</a>
*/
public static int conditionalSetClear(int w, int m, boolean flag) {
int f = flag ? -1 : 0;
return w ^ ((f ^ w) & m);
}

8. 无分支地条件性取反一个值#

/**
* ### 10. 无分支地条件性取反一个值 ###
* 等效于: fNegate ? -v : v;
* @param v 待取反的值。
* @param fNegate 是否取反的标志。
* @return 结果。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#ConditionalNegate">Original C version</a>
*/
public static int conditionalNegate(int v, boolean fNegate) {
int f = fNegate ? -1 : 0;
return (v ^ f) - f;
}

9. 根据掩码合并两个值的位#

/**
* ### 11. 根据掩码合并两个值的位 ###
* 等效于: (a & ~mask) | (b & mask);
* @param a 当掩码位为0时取此值。
* @param b 当掩码位为1时取此值。
* @param mask 掩码。
* @return 合并后的值。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge">Original C version</a>
*/
public static int mergeBits(int a, int b, int mask) {
return a ^ ((a ^ b) & mask);
}

10. 计算置位数 (Kernighan)#

/**
* ### 14. 计算置位数 (Brian Kernighan's way) ###
* 循环次数等于置位(1)的数量。
* @param v 待计算的整数。
* @return 置位(1)的数量。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan">Original C version</a>
*/
public static int countSetBits(int v) {
int c = 0;
while (v != 0) {
v &= (v - 1);
c++;
}
return c;
}

11. 并行计算置位数#

/**
* ### 16. 并行计算置位数 ###
* @param v 待计算的32位整数。
* @return 置位(1)的数量。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel">Original C version</a>
*/
public static int countSetBitsParallel(int v) {
v = v - ((v >>> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
return (((v + (v >>> 4)) & 0x0F0F0F0F) * 0x01010101) >>> 24;
}

12. 并行计算奇偶校验位#

/**
* ### 23. 并行计算奇偶校验位 ###
* @param v 待计算的32位整数。
* @return 如果1的个数为奇数返回true, 否则返回false。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#ParityParallel">Original C version</a>
*/
public static boolean getParity(int v) {
v ^= v >>> 16;
v ^= v >>> 8;
v ^= v >>> 4;
v &= 0x0f;
return ((0x6996 >>> v) & 1) != 0;
}

13. 通过异或(XOR)交换两个整数的值#

/**
* ### 25. 通过异或(XOR)交换两个整数的值 ###
* 注意:a和b不能是同一个内存地址的引用。
* @param a 第一个整数的单元素数组。
* @param b 第二个整数的单元素数组。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#SwappingValuesXOR">Original C version</a>
*/
public static void swapWithXOR(int[] a, int[] b) {
if (a == b) return;
a[0] ^= b[0];
b[0] ^= a[0];
a[0] ^= b[0];
}

14. 通过异或(XOR)交换一个整数内部的两个位序列#

/**
* ### 26. 通过异或(XOR)交换一个整数内部的两个位序列 ###
* @param b 原始整数。
* @param i 第一个位序列的起始位置。
* @param j 第二个位序列的起始位置。
* @param n 位序列的长度。
* @return 交换后的整数。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#SwappingBitsXOR">Original C version</a>
*/
public static int swapBitSequences(int b, int i, int j, int n) {
int x = ((b >>> i) ^ (b >>> j)) & ((1 << n) - 1);
return b ^ ((x << i) | (x << j));
}

15. 并行反转一个32位整数的所有位#

/**
* ### 32. 并行反转一个32位整数的所有位 ###
* @param v 待反转的32位整数。
* @return 反转后的整数。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel">Original C version</a>
*/
public static int reverseBits(int v) {
v = ((v >>> 1) & 0x55555555) | ((v & 0x55555555) << 1);
v = ((v >>> 2) & 0x33333333) | ((v & 0x33333333) << 2);
v = ((v >>> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4);
v = ((v >>> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8);
v = (v >>> 16) | (v << 16);
return v;
}

16. 计算 n % d, 其中 d = 2^s - 1#

/**
* ### 34. 计算 n % d, 其中 d = 2^s - 1 ###
* @param n 分子。
* @param s d = (1 << s) - 1。
* @return n % d。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#ModulusDivision">Original C version</a>
*/
public static int modulusByPowerOf2Minus1(int n, int s) {
if (s <= 0 || s >= 32) throw new IllegalArgumentException("s must be between 1 and 31");
int d = (1 << s) - 1;
if (d == 0) return 0;
int m = n;
while (n > d) {
m = 0;
int tempN = n;
while (tempN > 0) {
m += tempN & d;
tempN >>>= s;
}
n = m;
}
return m == d ? 0 : m;
}

17. 使用 De Bruijn 序列计算log₂#

/**
* ### 40. 使用 De Bruijn 序列和乘法查找计算整数的log₂ ###
* @param v 待计算的32位正整数。
* @return log₂(v)。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn">Original C version</a>
*/
public static int log2WithDeBruijn(int v) {
final int[] DE_BRUIJN_TABLE = {
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};
v |= v >>> 1;
v |= v >>> 2;
v |= v >>> 4;
v |= v >>> 8;
v |= v >>> 16;
return DE_BRUIJN_TABLE[(v * 0x07C4ACDD) >>> 27];
}

18. 计算整数的log₁₀#

/**
* ### 41. 计算整数的log₁₀ ###
* @param v 待计算的32位正整数。
* @return log₁₀(v)。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10">Original C version</a>
*/
public static int log10(int v) {
if (v <= 0) throw new IllegalArgumentException("v must be positive");
final int[] POWERS_OF_10 = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
int log2 = 31 - Integer.numberOfLeadingZeros(v);
int t = (log2 + 1) * 1233 >>> 12;
return t - (v < POWERS_OF_10[t] ? 1 : 0);
}

19. 计算末尾连续0的数量#

/**
* ### 50. 使用 De Bruijn 序列和乘法查找计算末尾连续0的数量 ###
* @param v 待计算的32位整数。
* @return 末尾连续0的数量。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup">Original C version</a>
*/
public static int countTrailingZeros(int v) {
final int[] DE_BRUIJN_TABLE = {
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
if (v == 0) return 32;
return DE_BRUIJN_TABLE[(((v & -v) * 0x077CB531)) >>> 27];
}

20. 向上取整到下一个2的幂#

/**
* ### 52. 向上取整到下一个2的幂 ###
* @param v 待计算的32位整数。
* @return 大于或等于v的最小的2的幂。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2">Original C version</a>
*/
public static int roundUpToPowerOfTwo(int v) {
if (v == 0) return 1;
v--;
v |= v >>> 1;
v |= v >>> 2;
v |= v >>> 4;
v |= v >>> 8;
v |= v >>> 16;
v++;
return v;
}

21. 交错位 (莫顿码)#

/**
* ### 56. 使用二进制魔数交错两个16位整数的位,生成32位莫顿码 ###
* @param x 第一个16位整数 (放入偶数位)。
* @param y 第二个16位整数 (放入奇数位)。
* @return 32位莫顿码。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN">Original C version</a>
*/
public static int interleaveBits(int x, int y) {
//x and y must be < 65536
x = (x | (x << 8)) & 0x00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F;
x = (x | (x << 2)) & 0x33333333;
x = (x | (x << 1)) & 0x55555555;
y = (y | (y << 8)) & 0x00FF00FF;
y = (y | (y << 4)) & 0x0F0F0F0F;
y = (y | (y << 2)) & 0x33333333;
y = (y | (y << 1)) & 0x55555555;
return x | (y << 1);
}

22. 判断一个字中是否含有0字节#

/**
* ### 57. 判断一个字中是否含有0字节 ###
* @param v 待检查的整数。
* @return 如果含有0字节则返回true。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord">Original C version</a>
*/
public static boolean hasZeroByte(int v) {
return ((v - 0x01010101) & ~v & 0x80808080) != 0;
}

23. 判断一个整数中是否有等于n的字节#

/**
* ### 58. 判断一个整数中是否有等于n的字节 ###
* @param x 待检查的整数。
* @param n 目标字节值 (0-255)。
* @return 如果含有等于n的字节则返回true。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#ValueInWord">Original C version</a>
*/
public static boolean hasValueByte(int x, int n) {
int mask = 0x01010101 * (n & 0xFF);
// 参考 22. 判断一个字中是否含有0字节
return hasZeroByte(x ^ mask);
}

24. 判断一个字中是否有小于n的字节#

/**
* ### 59. 判断一个字中是否有小于n的字节 ###
* @param x 待检查的整数。
* @param n 比较值 (0-128)。
* @return 如果含有小于n的字节则返回true。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord">Original C version</a>
*/
public static boolean hasByteLessThan(int x, int n) {
return ((x - (0x01010101 * n)) & ~x & 0x80808080) != 0;
}

25. 计算字典序的下一个位排列#

/**
* ### 62. 计算字典序的下一个位排列 ###
* @param v 当前的位排列。
* @return 下一个位排列。
* @see <a href="https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation">Original C version</a>
*/
public static int nextBitPermutation(int v) {
int t = v | (v - 1);
int w = (t + 1) | (((~t & -~t) - 1) >>> (Integer.numberOfTrailingZeros(v) + 1));
return w;
}
`catcodeme`: generate by signature.cnrad.dev, animation by Claude AI
位运算技巧整理
https://8cat.life/blog/test.html
catcodeme Author catcodeme
Published at August 13, 2025