如果您真的关心性能,清除 msb 的最佳方法最近已针对 x86 进行了更改,并添加了 BMI 指令。
在 x86 程序集中:
clear_msb:
bsrq %rdi, %rax
bzhiq %rax, %rdi, %rax
retq
现在用 C 重写并让编译器发出这些指令,同时优雅地降级非 x86 架构或不支持 BMI 指令的旧 x86 处理器。
与汇编代码相比,C 版本实在是丑陋而冗长。但至少它满足了便携性的目标。如果你有必要的硬件和编译器指令(-mbmi、-mbmi2)来匹配,编译后你又回到了漂亮的汇编代码。
如所写, bsr() 依赖于内置的 GCC/Clang。如果针对其他编译器,您可以用等效的可移植 C 代码和/或不同的编译器特定的内置函数替换。
#include <inttypes.h>
#include <stdio.h>
uint64_t bsr(const uint64_t n)
{
return 63 - (uint64_t)__builtin_clzll(n);
}
uint64_t bzhi(const uint64_t n,
const uint64_t index)
{
const uint64_t leading = (uint64_t)1 << index;
const uint64_t keep_bits = leading - 1;
return n & keep_bits;
}
uint64_t clear_msb(const uint64_t n)
{
return bzhi(n, bsr(n));
}
int main(void)
{
uint64_t i;
for (i = 0; i < (uint64_t)1 << 16; ++i) {
printf("%" PRIu64 "\n", clear_msb(i));
}
return 0;
}
正如最初的问题所提出的那样,汇编和 C 版本都可以自然地被 32 位指令取代。