我想尽快执行以下操作
x / LSB(x)
,其中x
是编译时未知的整数值,LSB(x) = x & -x
。(或者,该操作相当于偶数除以2的最高幂<= x。)我正在寻找一个合理的可移植的解决方案(没有编译器的内在/内置像GCC的__builtin_clz
或类似的)。
我担心的是下面的简单实现
x / (x & -x)
仍然会导致开销很大的除法,因为编译器可能没有意识到除法实际上相当于向右移动除数后面的0的个数。
如果我的担心是合理的,那么实现它的更有效的方法是什么?
我将欣赏一个解决方案,很容易扩展到整数类型的大小32位,64位,128位,…
x >>= ffs(x)-1;
ffs
函数符合4.3BSD, POSIX.1-2001。
如果x
为0,它将不起作用。
如果您不想依赖于CLZ (count前导零)硬件指令,您可以按照本答案中描述的方式计算前导零。查找和乘以一个神奇的数字是非常快的。我将在这里重新发布代码:
unsigned x; // input to clz
unsigned c; // output of clz
static const unsigned MultiplyDeBruijnBitPosition[32] =
{
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
};
c = MultiplyDeBruijnBitPosition[((unsigned)((x & -x) * 0x077CB531U)) >> 27];
计算完前导零后,就不再需要使用除法指令了。相反,您可以通过c
向右移动值。也就是说(去掉一个不需要的临时值),代码变成这样:
static const unsigned MultiplyDeBruijnBitPosition[32] =
{
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
};
x >>= MultiplyDeBruijnBitPosition[((unsigned)((x & -x) * 0x077CB531U)) >> 27]; // x /= LSB(x)