c++ - C++ 中的高效整数下限函数
问题描述
我想定义一个有效的整数下限函数,即从浮点数或双精度数执行截断到负无穷大的转换。
我们可以假设这些值不会发生整数溢出。到目前为止,我有几个选择
转换为 int;这需要对负值进行特殊处理,因为强制转换趋向于零;
I= int(F); if (I < 0 && I != F) I--;
将 floor 的结果转换为 int;
int(floor(F));
转换为 int 并进行较大的转换以获得正数(这可能会为大值返回错误的结果);
int(F + double(0x7fffffff)) - 0x7fffffff;
转换为 int 是出了名的慢。if 测试也是如此。我没有计时地板功能,但看到帖子声称它也很慢。
您能在速度、准确性或允许范围方面想出更好的选择吗?它不需要是便携式的。目标是最近的 x86/x64 架构。
解决方案
转换为 int 是出了名的慢。
也许你从 x86-64 开始就一直生活在一块石头下,或者以其他方式错过了在 x86 上这已经有一段时间了。:)
SSE/SSE2 有一个使用截断转换的指令(而不是默认的舍入模式)。ISA 有效地支持此操作正是因为在实际代码库中使用 C 语义进行转换并不罕见。x86-64 代码使用 SSE/SSE2 XMM 寄存器进行标量 FP 数学,而不是 x87,因为这个和其他使它更高效的东西。即使是现代 32 位代码也使用 XMM 寄存器进行标量数学运算。
在为 x87 编译(没有 SSE3 fisttp
)时,编译器过去必须将 x87 舍入模式更改为截断,FP 存储到内存,然后再次更改舍入模式。(然后从内存中重新加载整数,通常是从堆栈上的本地,如果用它做进一步的事情。)x87 对此很糟糕。
是的,这非常慢,例如在 2006年编写@Kirjain 答案中的链接时,如果您仍然拥有 32 位 CPU 或使用 x86-64 CPU 运行 32 位代码。
不直接支持使用截断或默认(最近)以外的舍入模式进行转换,直到 SSE4.1 roundps
/roundpd
你最好的选择是魔术数字技巧,如@Kirjain 答案中的 2006 链接。
那里有一些不错的技巧,但仅适用于double
-> 32 位整数。double
如果你有,不太可能值得扩展float
。
或者更常见的是,只需添加一个大数字来触发舍入,然后再次减去它以返回原始范围。这可以在float
不扩展到的情况下工作double
,但我不确定制作floor
工作有多容易。
无论如何,这里明显的解决方案是_mm256_floor_ps()
and _mm256_cvtps_epi32
( vroundps
and vcvtps2dq
)。它的非 AVX 版本可以与 SSE4.1 一起使用。
我不确定我们是否可以做得更好;如果您有一个庞大的数组要处理(并且无法设法将这项工作与其他工作交错),您可以将 MXCSR 舍入模式设置为“towards -Inf”(地板)并简单地使用vcvtps2dq
(它使用当前的舍入模式) . 然后放回去。但是最好缓存阻止您的转换或在生成数据时即时执行,可能来自需要将 FP 舍入模式设置为默认最近的其他 FP 计算。
roundps
/pd/ss/sd 在 Intel CPU 上是 2 uop,但在 AMD Ryzen 上只有 1 uop(每个 128 位通道)。 cvtps2dq
也是 1 uop。打包的 double->int 转换还包括随机播放。标量 FP->int 转换(复制到整数寄存器)通常也为此花费额外的微指令。
因此,在某些情况下,魔术数字技巧有可能获胜;如果_mm256_floor_ps()
+ cvt 是关键瓶颈的一部分(或者如果您有 double 并且想要 int32,则更有可能)可能值得调查。
@Cássio Renan'sint foo = floorf(f)
实际上会自动矢量化,如果使用gcc -O3 -fno-trapping-math
(或-ffast-math
)编译,使用-march=
具有 SSE4.1 或 AVX 的东西。 https://godbolt.org/z/ae_KPv
如果您将它与其他未手动矢量化的标量代码一起使用,这可能会很有用。特别是如果您希望编译器能够自动矢量化整个事情。
推荐阅读
- python - Python Web Scraper IEEE
- python - 如何为 python 竞争性编程环境设置 Sublime Text
- json - 元掩码错误:内部 JSON-RPC 错误。在 metamask 手机上确认时
- google-sheets - Google 表格:TypeError:无法读取未定义的属性“范围”
- python - 将对象形式的日期转换为整数和年份的样本
- javascript - 如何在渲染后只调用一次函数?
- python - 在特定时间段内的日期上添加一个小时 Pandas
- c# - 使用实体框架联合复合时使两列唯一
- c# - 您正在尝试使用“新”关键字创建 MonoBehaviour。这是不允许的
- html - 如何在网页中添加背景图片?