5

如何取消设置一个字的最高有效位(例如 0x00556844 -> 0x00156844)?gcc 中有一个__builtin_clz,但它只计算零,这对我来说是不需要的。另外,我应该如何将 __builtin_clz 替换为 msvc 或 intel c 编译器?

当前我的代码是

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

更新:好的,如果你说这段代码相当快,我会问你,我应该如何为这段代码添加可移植性?此版本适用于 GCC,但 MSVC 和 ICC?

4

3 回答 3

7

只需向下舍入到最接近的 2 次幂,然后将其与原始值进行异或,例如使用flp2()来自Hacker's Delight

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 重写并让编译器发出这些指令,同时优雅地降级非 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 位指令取代。

于 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 回答