19

以下代码在 GCC 中调用 clz/ctz 的内置函数,并且在其他系统上具有 C 版本。显然,如果系统具有内置的 clz/ctz 指令,如 x86 和 ARM,则 C 版本有点不理想。

#ifdef __GNUC__
#define clz(x) __builtin_clz(x)
#define ctz(x) __builtin_ctz(x)
#else
static uint32_t ALWAYS_INLINE popcnt( uint32_t x )
{
    x -= ((x >> 1) & 0x55555555);
    x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
    x = (((x >> 4) + x) & 0x0f0f0f0f);
    x += (x >> 8);
    x += (x >> 16);
    return x & 0x0000003f;
}
static uint32_t ALWAYS_INLINE clz( uint32_t x )
{
    x |= (x >> 1);
    x |= (x >> 2);
    x |= (x >> 4);
    x |= (x >> 8);
    x |= (x >> 16);
    return 32 - popcnt(x);
}
static uint32_t ALWAYS_INLINE ctz( uint32_t x )
{
    return popcnt((x & -x) - 1);
}

#endif

我需要调用哪些函数,需要包含哪些标头等,以便在此处为 MSVC 添加适当的 ifdef?我已经看过这个页面,但我不完全确定 #pragma 的用途(是否需要?)以及它对 MSVC 版本编译要求的限制。作为一个不真正使用 MSVC 的人,我也不知道这些内在函数在其他架构上是否具有 C 等价物,或者在 #defining 它们时我是否也必须 #ifdef x86/x86_64。

4

6 回答 6

25

从 sh0dan 代码中弹起,应该像这样更正实现:

#ifdef _MSC_VER
#include <intrin.h>

uint32_t __inline ctz( uint32_t value )
{
    DWORD trailing_zero = 0;

    if ( _BitScanForward( &trailing_zero, value ) )
    {
        return trailing_zero;
    }
    else
    {
        // This is undefined, I better choose 32 than 0
        return 32;
    }
}

uint32_t __inline clz( uint32_t value )
{
    DWORD leading_zero = 0;

    if ( _BitScanReverse( &leading_zero, value ) )
    {
       return 31 - leading_zero;
    }
    else
    {
         // Same remarks as above
         return 32;
    }
}
#endif

如代码中所述,如果值为 0,则 ctz 和 clz 都是未定义的。在我们的抽象中,我们固定__builtin_clz(value)(value?__builtin_clz(value):32)但它是一个选择

于 2013-12-09T10:23:48.750 回答
1

如果 MSVC 有一个内在的编译器,它会在这里:

MSDN 上的编译器内在函数

否则,您必须使用 __asm 编写它

于 2008-12-10T17:33:21.667 回答
1
  1. MSVC 中int __builtin_ctz (unsigned int x)的等效函数是unsigned int _tzcnt_u32 (unsigned int a)用于32 位整数并返回尾随零的计数。对于64 位使用unsigned __int64 _tzcnt_u64 (unsigned __int64 a) 1

  2. MSVC 中int __builtin_clz (unsigned int x)的等效函数是unsigned int _lzcnt_u32 (unsigned int a)用于32 位整数并返回前导零的计数。对于64 位使用unsigned __int64 _lzcnt_u64 (unsigned __int64 a) 2

C++ 头文件:immintrin.h

于 2021-05-03T16:35:40.763 回答
1

我在韩国网站https://torbjorn.tistory.com/317中找到它, 在 msvc 编译器中,您可以在 gcc 编译器中使用__lzcnt(unsigned int)替换。__builtin_clz(unsigned int)

于 2020-10-17T02:07:39.203 回答
-3

在 linux 和 windows (x86) 上测试:

#ifdef WIN32
    #include <intrin.h>
    static uint32_t __inline __builtin_clz(uint32_t x) {
        unsigned long r = 0;
        _BitScanReverse(&r, x);
        return (31-r);
    }
#endif

uint32_t clz64(const uint64_t x)
{
    uint32_t u32 = (x >> 32);
    uint32_t result = u32 ? __builtin_clz(u32) : 32;
    if (result == 32) {
        u32 = x & 0xFFFFFFFFUL;
        result += (u32 ? __builtin_clz(u32) : 32);
    }
    return result;
}
于 2014-09-13T12:17:42.713 回答
-4

有两个内在函数“_BitScanForward”和“_BitScanReverse”,它们适用于 MSVC 的相同目的。包括 。功能是:

#ifdef _MSC_VER
#include <intrin.h>

static uint32_t __inline ctz( uint32_t x )
{
   int r = 0;
   _BitScanReverse(&r, x);
   return r;
}

static uint32_t __inline clz( uint32_t x )
{
   int r = 0;
   _BitScanForward(&r, x);
   return r;
}
#endif

有等效的 64 位版本“_BitScanForward64”和“_BitScanReverse64”。

在这里阅读更多:

MSDN 上的 x86 内部函数

于 2011-03-29T06:44:51.343 回答