パフォーマンスに本当に関心がある場合は、msb をクリアする最善の方法が最近 x86 用に変更され、BMI 命令が追加されました。
x86 アセンブリの場合:
clear_msb:
bsrq %rdi, %rax
bzhiq %rax, %rdi, %rax
retq
ここで、C で書き直して、BMI 命令をサポートしていない x86 以外のアーキテクチャまたは古い 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 ビット命令に置き換えられるのに自然に役立ちます。