c - 如何计算 32 位无符号整数中的前导零

标签 c 32-bit unsigned-integer leading-zero

<分区>

谁能告诉我在 C 编程中计算 32 位无符号整数中前导零的数量的有效算法是什么?

最佳答案

此讨论假定您的编译器不支持该操作,或者它不能产生足够好的汇编。请注意,现在这两种情况都不太可能发生,所以我建议只使用 __builtin_clz对于 gcc 或编译器上的等效项。

请注意,确定哪个是“最佳”clz 算法只能由您来完成。现代处理器是复杂的野兽,这些算法的性能在很大程度上取决于你运行它的平台、你扔给它的数据以及使用它的代码。唯一可以确定的方法是测量、测量和测量更多。如果您无法分辨出差异,那么您可能没有在寻找瓶颈,您的时间最好花在其他地方。

现在无聊的免责声明已经被排除在外,让我们看看Hacker's Delight是什么不得不说的问题。快速调查表明,所有算法都依赖于某些描述的二分搜索。这是一个简单的例子:

int n = 32;
unsigned y;

y = x >>16; if (y != 0) { n = n -16; x = y; }
y = x >> 8; if (y != 0) { n = n - 8; x = y; }
y = x >> 4; if (y != 0) { n = n - 4; x = y; }
y = x >> 2; if (y != 0) { n = n - 2; x = y; }
y = x >> 1; if (y != 0) return n - 2;
return n - x;

请注意,这适用于 32 位整数,并且如果需要,它也可以转换为迭代版本。不幸的是,该解决方案没有大量的指令级并行性,并且有相当多的分支不能构成一个非常好的位旋转算法。请注意,存在上述代码的无分支版本,但它更加冗长,因此我不会在此处重现。

那么让我们通过使用 pop 指令(计算位数)来改进解决方案:

x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >>16);
return pop(~x);

那么这是如何工作的呢?关键是末尾的 pop(~x) 指令,它计算了 x 中零的个数。为了使零的数量有意义,我们首先需要摆脱所有不领先的 0。我们通过使用二进制算法向右传播 1 来做到这一点。虽然我们仍然没有太多的指令级并行性,但我们确实摆脱了所有分支,并且它比以前的解决方案使用更少的周期。好多了。

那么那个pop指令呢,那不是作弊吗?大多数架构都有一个 1 周期弹出指令,可以通过编译器内置指令访问(例如 gcc 的 __builtin_pop)。否则存在基于表的解决方案,但在权衡缓存访问的周期时必须小心,即使表完全保存在 L1 缓存中也是如此。

最后,就像黑客的喜悦一样,我们开始在陌生的领域游荡。让我们用 float 来计算一些前导零:

union {
    unsigned asInt[2];
    double asDouble;
};
asDouble = (double)k + 0.5;
return 1054 - (asInt[LE] >> 20);

首先,一个小警告:不要使用这个算法。就标准而言,这会触发未定义的行为。与任何实际用途相比,这是为了娱乐因素而重现的。使用后果自负。

既然免责声明已经过时,它是如何运作的?它首先将 int 转换为 double,然后继续提取 double 的指数部分。整洁的东西。如果在小端机器上执行,LE 常量应为 1,在大端机器上应为 0。

这应该让您简要了解解决此问题的各种位旋转算法。请注意,本书有这些内容的多种变体,需要进行各种权衡,但我会让您自己发现这些内容。

关于c - 如何计算 32 位无符号整数中的前导零,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23856596/

相关文章:

python - 为什么 reversed(mylist) 这么慢?

c++ - 在指针中存储无符号整数

c - 用 N 个不可靠位计算 unsigned int 可能值的最快方法?

计算 C 中 char 数组的离散值

iphone - AudioQueue 捕获并返回不同的缓冲区大小

c++ - 在没有 Arduino 库的情况下获取引脚输入

Java 32 位 JRE : can this JRE run a 64 bit program?

python - Pyside2 32 位 arm Linux

c++ - 依赖从任何数字类型(unsigned、int、...)到 double 的隐式提升是否安全?

c - 旧 C 代码需要左值作为增量操作数错误