您好我正在尝试创建一个宏来计算C中数字的基数2的对数。该数字应该是#defined的表的大小,如下所示。
我搜索了一下,发现这个网站包含了base2log的实现 https://graphics.stanford.edu/~seander/bithacks.html
uint8_t base2log(uint16_t value){
uint8_t result = 0; // r will be lg(v)
while (value >>= 1)
{
result++;
}
return result;
}
我的第一个想法是现在创建宏:
#define SIZE 256
#define BASE2LOG (base2log(SIZE))
...但这看起来不是一个非常优雅的解决方案,因为即使BASE2LOG应该在编译时定义,它仍然需要每次在代码中出现时调用一个函数。我想过可能在全局变量中分配BASE2LOG,但我确信必须有比这更简洁和正确的东西。
有办法做到这一点吗?
感谢您的时间。
答案 0 :(得分:2)
这里是一个纯宏解决方案,允许在编译时计算对数,并在C中预期整数常量表达式时使用 - 例如,当指定a的长度时(非可变长度)数组。 整数常量表达式只是编译器必须能够在编译时评估的表达式的标准。
我已经在其他地方看到了这种变体(例如Macro to compute number of bits needed to store a number n中有一个),所以我无法获得荣誉。我至少可以写出它的工作原理。
// Computes the base 2 logarithm of a 16-bit number. Meant to be used
// at compile time.
#define LOG2(n) ((n) & 0xFF00 ? 8 + LOG2_8((n) >> 8) : LOG2_8(n))
#define LOG2_8(n) ((n) & 0xF0 ? 4 + LOG2_4((n) >> 4) : LOG2_4(n))
#define LOG2_4(n) ((n) & 0xC ? 2 + LOG2_2((n) >> 2) : LOG2_2(n))
#define LOG2_2(n) ((n) & 0x2 ? 1 : 0)
数字的截断基数2对数只是最高1位的索引(0是最低有效位的索引)。要找到最高的1位,可以使用一种二进制搜索。
在LOG2()
(主宏)中,我们使用(n) & 0xFF00 ? ...
来测试高字节是否包含1位。如果是,那么n
中最高1位的索引是8加上n
高字节内最高1位的索引。如果它不是,则最高1位的索引只是低位字节中最高1位的索引。
要获得高字节,我们执行(n) >> 8
。其余的宏仅查看低字节,因此不需要屏蔽。
LOG2_8()
宏计算一个字节中最高1位的索引。它使用与上述相同的逻辑,间隔减半。如果高4位包含1位,则最高1位的索引是4加上高4位内最高1位的索引。否则,它是低4位内最高位的索引。
LOG_4()
的工作原理完全相同。
对于基本案例,LOG2_2(1) == 0
和LOG2_2(2) == 1
。 LOG2_2(0)
(在数学上未定义)恰好也变为0。
宏可以通用化,以显而易见的方式处理更大的类型。移动大于类型宽度的值是严格未定义的(不确定编译器在实践中的可靠性)并且需要注意。使其安全的一种方法是添加一个演员(假设C99 +):
#define LOG2(n) LOG2_64((uint64_t)(n))
#define LOG2_64(n) ... /* as usual. */
...
也可以使用更简单(也有点垃圾邮件)的解决方案,例如
#define LOG2(n) \
((n) < 2 ? 0 : \
(n) < 4 ? 1 : \
(n) < 8 ? 2 : \
(n) < 16 ? 3 : \
(n) < 32 ? 4 : \
(n) < 64 ? 5 : \
...
(顺便提一下,C99有//
式的评论,万一有人觉得有点抱怨。;)
答案 1 :(得分:2)
另一个解决方案。如果数字超过最大值,则此错误提供错误。
#define MIN_BITS(n) (1+(n>1)+(n>3)+(n>7)+(n>0x0f)+(n>0x1f)+(n>0x3f)+(n>0x7f)+ \
(n>0x0ff)+(n>0x1ff)+(n>0x3ff)+(n>0x7ff)+ \
(n>0xfff)+(n>0x1fff)+(n>0x3fff)+(n>0x7fff)+ \
((n>0xffff)?100:0))