求解一个数的二进制最高位

2024-07-12 16:01

求解一个数的二进制最高位是一个常见问题。具体来说,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位(即表示符号的最高位),上面的函数均没有考虑负数情况,请多加注意。

DeepSeek 开源周回顾「GitHub 热点速览」 物流快递公司核心技术能力-地址解析分单基础技术分享 .NET 10首个预览版发布:重大改进与新特性概览! AI与.NET技术实操系列(二):开始使用ML.NET 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示