5

ワードの最上位の設定ビットを設定解除するにはどうすればよいですか (例: 0x00556844 -> 0x00156844)? __builtin_clzgccにはaがありますが、ゼロをカウントするだけで、私には必要ありません。また、msvc または intel c コンパイラの __builtin_clz をどのように置き換える必要がありますか?

現在の私のコードは

 int msb = 1<< ((sizeof(int)*8)-__builtin_clz(input)-1);
 int result = input & ~msb;

更新: わかりました。このコードがかなり高速であると言う場合は、このコードに移植性を追加するにはどうすればよいですか? このバージョンは GCC 用ですが、MSVC & ICC ですか?

4

3 に答える 3

7

最も近い 2 の累乗に切り捨ててから、元の値と XOR します。たとえば、 Hacker's Delightflp2()を使用します。

uint32_t flp2(uint32_t x) // round x down to nearest power of 2
{
    x = x | (x >> 1); 
    x = x | (x >> 2); 
    x = x | (x >> 4); 
    x = x | (x >> 8); 
    x = x | (x >>16); 
    return x - (x >> 1); 
}

uint32_t clr_msb(uint32_t x) // clear most significant set bit in x
{
    msb = flp2(x);  // get MS set bit in x
    return x ^ msb; // XOR MS set bit to clear it
}
于 2011-05-15T20:56:51.690 に答える
6

パフォーマンスに本当に関心がある場合は、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 ビット命令に置き換えられるのに自然に役立ちます。

于 2015-08-12T06:16:22.150 に答える
3

できるよ

unsigned resetLeadingBit(uint32_t x) {
    return x & ~(0x80000000U >> __builtin_clz(x))
}

MSVC には_BitScanReverseがあり、これは 31-__builtin_clz() です。

実際にはその逆で、BSR は自然な x86 命令であり、gcc 組み込みは 31-BSR として実装されています。

于 2011-05-15T20:58:36.250 に答える