将 64 位整数加载到双精度 SSE2 寄存器的最佳方法?

Posted

技术标签:

【中文标题】将 64 位整数加载到双精度 SSE2 寄存器的最佳方法?【英文标题】:Best way to load a 64-bit integer to a double precision SSE2 register? 【发布时间】:2013-03-22 11:16:56 【问题描述】:

在 32 位模式下将 64 位整数值加载到 xmm SSE2 寄存器中的最佳/最快方法是什么?

在 64 位模式下,cvtsi2sd 可以使用,但在 32 位模式下,它只支持 32 位整数。

到目前为止,我还没有找到更多的东西:

使用fildfstp 堆叠然后movsdxmm 寄存器 加载高 32 位部分,乘以 2^32,加上低 32 位

第一个解决方案很慢,第二个解决方案可能会引入精度损失(edit:无论如何它很慢,因为必须将低 32 位转换为无符号...)

有更好的方法吗?

【问题讨论】:

在浮点数中将前 32 位乘以 2**32 不会截断/舍入它们。只有当您将低 32 位添加到它们时,总和才会被舍入/截断,这就是您使用第一种方法得到的结果。除非我遗漏了什么,否则这两种方法是等效的(性能除外)。 FWIW gcc 似乎使用第一种方法(fild、fst、movsd)。 第二个选项其实很慢,我错误地将cvtsi2sd用于低32位,但这是不正确的,它需要转换为无符号,没有CPU指令存在,所以它很慢。 .. IEEE doubles 和魔术常数的内部表示有一个技巧,例如:software.intel.com/en-us/forums/topic/301988,但不知道速度 这里有更好的解释(对于未签名):***.com/questions/13734191/… 【参考方案1】:

你的第二个选项可以工作,虽然它有点笨拙。我假设您的 64 位数字最初位于 edx:eax 中。

cvtsi2sd xmm0, edx              // high part * 2**-32
mulsd    xmm0, [2**32 from mem] // high part
movsd    xmm2, [2**52 from mem]
movd     xmm1, eax
orpd     xmm1, xmm2             // (double)(2*52 + low part as unsigned)
subsd    xmm1, xmm2             // (double)(low part as unsigned)
addsd    xmm0, xmm1             // (double)(high part + low part as unsigned)

除了可能的最后一个之外的所有操作都是精确的,所以这是正确的四舍五入。应该注意的是,当输入为0 并且mxcsr 设置为round-to-minus-infinity 时,此转换会产生-0.0。如果它被用于旨在提供 IEEE-754 一致性的编译器的运行时库中,则需要解决此问题,但对于大多数使用而言这不是问题。

【讨论】:

以上是关于将 64 位整数加载到双精度 SSE2 寄存器的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

两个 SSE2 压缩双精度的最优无分支条件选择

如何使用 SSE2 加载 16 x 8 位整数

用于灰度到 ARGB 转换的 C++ SSE2 或 AVX2 内在函数

如何在 x86(32 位)程序集中将无符号整数转换为浮点数?

如何将两个打包的 64 位四字加载到 128 位 xmm 寄存器中

变体浮点到双精度值转换,舍入到小数点后 1 位 [重复]