快速饱和整数转换?

Fast saturating integer conversion?

提问人:Joseph Garvin 提问时间:3/29/2021 最后编辑:Joseph Garvin 更新时间:3/30/2021 访问量:465

问:

我想知道是否有任何快速的位摆弄技巧来执行从 64 位无符号值到 32 位无符号值的饱和转换(如果它泛化到其他宽度会很好,但这是我关心的主要宽度)。我能够在谷歌上找到的大多数资源都用于饱和算术运算。

饱和转换将采用 64 位无符号值,如果输入值大于 2^32-1,则返回未修改的值作为 32 位值或 2^32-1。请注意,这不是默认的 C 强制转换截断行为。

我可以想象做这样的事情:

  • 测试上半部分是否设置了任何位
  • 如果是这样,请创建一个设置了所有位的 32 位掩码,否则创建一个未设置所有位的掩码
  • 按位或带掩码的下半部分

但是我不知道如何快速生成蒙版。我在 Godbolt 中尝试了简单的分支实现,看看编译器是否会为我生成一个聪明的无分支实现,但没有运气。

实现示例在这里。

#include <stdint.h>
#include <limits.h>

// Type your code here, or load an example.
uint32_t square(uint64_t num) {
    return num > UINT32_MAX ? UINT32_MAX : num;
}

编辑:我的错误,问题是godbolt没有设置为使用优化

性能 与语言无关 位操作 饱和-算术

评论

0赞 Joseph Garvin 3/29/2021
@trentcl它们都经过 AOT 编译并支持按位运算,因此可以轻易地将一个解决方案转换为另一个解决方案
0赞 dmuir 3/29/2021
如果 hi 是高 32 位,而 lo 是 lo 32 位,也许 mask = hi ?~lo : 0;lo ^= 掩码;可以编译为无分支
0赞 Joseph Garvin 3/29/2021
@KamilCuk编辑以包括
0赞 G. Sliepen 3/29/2021
@JosephGarvin 关于 godbolt 链接:您必须启用编译器优化,以便编译器生成最佳代码。

答:

4赞 G. Sliepen 3/29/2021 #1

你不需要做任何花哨的摆弄技巧来做到这一点。以下函数应该足以让编译器生成高效的代码:

uint32_t saturate(uint64_t value) {
    return value > UINT32_MAX ? UINT32_MAX : value;
}

这包含一个条件语句,但大多数常见的 CPU(如 AMD/Intel 和 Arm CPU)都有条件移动指令。因此,他们将测试溢出 32 位的值,并根据测试将其替换为 ,否则将其置之不理。例如,在 64 位 Arm 处理器上,此函数将由 GCC 编译(以:UINT32_MAX

saturate:
  mov x1, 4294967295
  cmp x0, x1
  csel x0, x0, x1, ls
  ret

请注意,您必须启用编译器优化才能获得上述结果。

评论

0赞 Joseph Garvin 3/29/2021
嗯,叮叮当当似乎不够聪明:godbolt.org/z/81oz1PYzd
0赞 Joseph Garvin 3/29/2021
我的错误,godbolt 没有设置为启用选项
1赞 Falk Hüffner 3/30/2021 #2

在不依赖条件移动的情况下执行此操作的一种方法是

((-(x >> 32)) | (x << 32)) >> 32