快速饱和整数转换?
Fast saturating integer conversion?
我想知道是否有任何快速的位技巧来进行从 64 位无符号值到 32 位无符号值的饱和转换(如果它被推广到其他宽度会很好,但那是我关心的主要宽度)。我能够通过谷歌搜索找到的大部分资源都用于饱和算术运算。
饱和转换将采用 64 位无符号值,并且 return 未修改为 32 位值的值或 2^32-1(如果输入值大于 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 没有设置为使用优化
你不需要做任何花哨的小动作来做到这一点。以下函数应该足以让编译器生成高效代码:
uint32_t saturate(uint64_t value) {
return value > UINT32_MAX ? UINT32_MAX : value;
}
这包含一个条件语句,但大多数常见的 CPU,如 AMD/Intel 和 Arm 的,都有 conditional move instructions。因此他们将测试溢出 32 位的值,并根据测试将其替换为 UINT32_MAX
,否则将其保留。例如,在 64 位 Arm 处理器上,此函数将由 GCC 编译(至:
saturate:
mov x1, 4294967295
cmp x0, x1
csel x0, x0, x1, ls
ret
请注意,您必须启用编译器优化才能获得上述结果。
一种不依赖条件移动的方法是
((-(x >> 32)) | (x << 32)) >> 32
我想知道是否有任何快速的位技巧来进行从 64 位无符号值到 32 位无符号值的饱和转换(如果它被推广到其他宽度会很好,但那是我关心的主要宽度)。我能够通过谷歌搜索找到的大部分资源都用于饱和算术运算。
饱和转换将采用 64 位无符号值,并且 return 未修改为 32 位值的值或 2^32-1(如果输入值大于 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 没有设置为使用优化
你不需要做任何花哨的小动作来做到这一点。以下函数应该足以让编译器生成高效代码:
uint32_t saturate(uint64_t value) {
return value > UINT32_MAX ? UINT32_MAX : value;
}
这包含一个条件语句,但大多数常见的 CPU,如 AMD/Intel 和 Arm 的,都有 conditional move instructions。因此他们将测试溢出 32 位的值,并根据测试将其替换为 UINT32_MAX
,否则将其保留。例如,在 64 位 Arm 处理器上,此函数将由 GCC 编译(至:
saturate:
mov x1, 4294967295
cmp x0, x1
csel x0, x0, x1, ls
ret
请注意,您必须启用编译器优化才能获得上述结果。
一种不依赖条件移动的方法是
((-(x >> 32)) | (x << 32)) >> 32