C99标准下计算unsigned int有效比特数的最优方法及合规性咨询
1. 关于“有效比特数是CHAR_BIT整数倍”的假设是否符合C99标准?
不符合。C99标准允许unsigned int类型存在填充比特——即不参与数值表示的额外比特,此时值比特数(用来表示数值的比特数)就不是CHAR_BIT的整数倍。
你的代码中,(unsigned int)-1会被转换为UINT_MAX,而UINT_MAX的定义是2^N - 1(N为值比特数),仅N个比特为1,剩余填充比特的内容不影响数值。如果存在填充比特,你的循环会把填充比特所在的字节也计入总数,导致结果偏大。
举个极端例子:假设CHAR_BIT=8,unsigned int宽度为3字节(24比特),但值比特数是17(剩余7个是填充比特),此时UINT_MAX=2^17-1(二进制17个1加7个0)。你的循环会执行3次,返回24,但实际值比特数是17,结果错误。
2. 更高效的编译期计算方法
严格遵循C99的话,完全可以在编译期算出值比特数,不需要运行时循环。核心思路是利用UINT_MAX(定义在<limits.h>中)的特性:UINT_MAX = 2^N - 1,N就是我们要的数值比特数。
方法一:多层条件判断(直观通用)
#include <limits.h> #define UINT_VALUE_BITS ( \ UINT_MAX == 0xFF ? 8 : \ UINT_MAX == 0xFFFF ? 16 : \ UINT_MAX == 0xFFFFFFFF ? 32 : \ UINT_MAX == 0xFFFFFFFFFFFFFFFF ? 64 : \ 0 /* 可根据需要扩展更大位数 */ \ )
方法二:位运算常量表达式(更灵活)
#include <limits.h> static const unsigned int uint_value_bits = (UINT_MAX & (UINT_MAX >> 1)) ? (UINT_MAX & (UINT_MAX >> 2)) ? (UINT_MAX & (UINT_MAX >> 4)) ? (UINT_MAX & (UINT_MAX >> 8)) ? (UINT_MAX & (UINT_MAX >> 16)) ? 32 : 16 : 8 : 4 : 2 : 1;
这个表达式通过不断右移并与原数做按位与,判断最高有效位的位置,编译期就能完成计算,完全符合C99标准。
3. 和Hamming weight算法的效率对比
你的运行时方法在无填充比特的场景下,循环次数等于sizeof(unsigned int)(比如32位系统是4次),确实比统计全1比特数的Hamming weight算法(循环32次)高效。但Hamming weight算法的优势是结果准确——因为UINT_MAX的置位比特数正好等于值比特数N,不受填充比特影响。
如果追求编译期计算,上面的常量表达式方法比两种运行时方法都高效,因为计算在编译阶段就完成了,运行时直接用常量即可。
内容的提问来源于stack exchange,提问作者Parminder Singh

