计算 128 位整数中前导零的数量

Counting the number of leading zeros in a 128-bit integer

如何有效地计算 128 位整数 (uint128_t) 中前导零的数量?

我知道 GCC 的内置函数:

但是,这些函数仅适用于 32 位和 64 位整数。

我还找到了一些SSE说明:

您可能猜到了,这些仅适用于 16、32 和 64 位整数。

对于 128 位整数是否有任何类似的、高效的内置功能?

假设 'random' 分布,第一个非零位将在高 64 位中,具有压倒性的概率,因此先测试那一半是有意义的。

查看为以下内容生成的代码:

/* inline */ int clz_u128 (uint128_t u)
{
    unsigned long long hi, lo; /* (or uint64_t) */
    int b = 128;

    if ((hi = u >> 64) != 0) {
        b = __builtin_clzll(hi);
    }
    else if ((lo = u & ~0ULL) != 0) {
        b = __builtin_clzll(lo) + 64;
    }

    return b;
}

我希望 gcc 使用 bsrq 指令实现每个 __builtin_clzll - 位扫描反向,即最高有效位位置 - 结合 xor, (msb ^ 63)sub(63 - msb),将其转换为前导零计数。 gcc 可能会使用正确的 -march=(体系结构)选项生成 lzcnt 指令。


编辑:其他人指出 'distribution' 在这种情况下不相关,因为无论如何都需要测试 HI uint64_t。

inline int clz_u128 (uint128_t u) {
  uint64_t hi = u>>64;
  uint64_t lo = u;
  int retval[3]={
    __builtin_clzll(hi),
    __builtin_clzll(lo)+64,
    128
  };
  int idx = !hi + ((!lo)&(!hi));
  return retval[idx];
}

这是一个无分支变体。请注意,与分支解决方案相比,完成了更多工作,实际上分支可能是可预测的。

它还依赖于 __builtin_clzll 喂 0 时不会崩溃:文档说结果未定义,但它只是未指定还是未定义?

只要 gcc 支持,Yakk 的答案就适用于所有类型的目标 目标的 128 位整数。但是,请注意,在 x86-64 平台上, 使用 Intel Haswell 处理器或更新的处理器,有一个更有效的解决方案:

#include <immintrin.h>
#include <stdint.h>
// tested with compiler options: gcc -O3 -Wall -m64  -mlzcnt

inline int lzcnt_u128 (unsigned __int128 u) {
  uint64_t hi = u>>64;
  uint64_t lo = u;
  lo = (hi == 0) ? lo : -1ULL;
  return _lzcnt_u64(hi) + _lzcnt_u64(lo);
}

_lzcnt_u64 内在编译 (gcc 5.4) 到 lzcnt 指令,很好 定义为零输入(它 returns 64),与 gcc 的 __builtin_clzll() 相反。 三元运算符编译为 cmove 指令。