求解一个数的二进制最高位
求解一个数的二进制最高位是一个常见问题。具体来说,5 的二进制是 101,其最高位在第 2 位(假定最低位是0)。30 的二进制是 11110,最高位是第 4 位。我们怎么求解这个位数呢?
方案一:逐位遍历
从低位向高位逐渐遍历即可,无需解释。当然也有很多种写法。这里提供一种。
int highestBit(int num) {
if (num == 0) return 0;
int pos = 0;
while (num > 1) {
num >>= 1;
pos++;
}
return pos;
}
方案二:部分打表
还有一个思路是空间换时间,对小范围的数据打表。假如我们首先计算出 lookup[256]
表示 0-255各自的最高位位置。由于 int 可以划分成8位为一块进行处理,我们先检查高16位是否有值,若有则只考虑高 16 位,否则只考虑低 16 位。同理再划分一次,我们则可以将范围缩小到 8 位,调用计算好的结果即可。
const int lookup[256] = {
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
};
int highestBit(int num) {
if (num == 0) return 0;
int pos = 0;
if (num & 0xFFFF0000) { pos += 16; num >>= 16; }
if (num & 0xFF00) { pos += 8; num >>= 8; }
return pos + lookup[num];
}
方案三:内置函数
在 C+ 中,GCC 和 Clang 编译器提供了一个内置函数 __builtin_clz
,用于计算一个整数的前导零的数量。结合这个函数,可以快速找到一个整数的最高位1的位置。
#include <climits> // for CHAR_BIT
int highestBit(int num) {
if (num == 0) return 0;
return sizeof(num) * CHAR_BIT - 1 - __builtin_clz(num);
}
__builtin_clz(num)
:返回整数 num
的前导零的数量。sizeof(num) * CHAR_BIT
:表示整数 num
的总位数。例如,对于32位整数,这个值是32。相减再减一就是正确答案。
使用 __builtin_clz
的效率通常非常高,这是因为它通常会被编译器翻译成一条高效的汇编指令。例如,在x86架构上,它通常被翻译成一条BSR(Bit Scan Reverse)或LZCNT(Leading Zero Count)指令,这些指令在硬件级别上非常高效。
大多数现代CPU都支持相关指令,在其他编译器(如MSVC)上可能需要不同的方法。可自行查阅。
注意处理负数
由于补码的性质,负数不存在最高位1。如果一定要定义一个,那么就是第31位(即表示符号的最高位),上面的函数均没有考虑负数情况,请多加注意。