有没有办法在带有 SSE 扩展的 x86 中将四个打包的单精度浮点值转换为四个双字?最接近的指令是CVTPS2PI,但它不能在两个 xmm 寄存器上执行,而是应该作为CVTPS2PI MM, XMM/M64
. 如果我想要类似的东西<conversion_mnemonic> XMM, XMM/M128
怎么办?
谢谢。伊曼。
有没有办法在带有 SSE 扩展的 x86 中将四个打包的单精度浮点值转换为四个双字?最接近的指令是CVTPS2PI,但它不能在两个 xmm 寄存器上执行,而是应该作为CVTPS2PI MM, XMM/M64
. 如果我想要类似的东西<conversion_mnemonic> XMM, XMM/M128
怎么办?
谢谢。伊曼。
x86 在 AVX512 之前没有对 FP<->unsigned 的原生支持,带有vcvtps2udq
( https://www.felixcloutier.com/x86/vcvtps2udq )。对于标量,您通常只需转换为 64 位有符号 ( cvtss2si rax, xmm0
) 并取其低 32 位(在 EAX 中),但这不是 SIMD 的选项。
如果没有 AVX-512,理想情况下您可以使用签名转换( cvtps2dq
) 并获得相同的结果。即,如果您的浮点数是非负数且 <=INT_MAX
( 2147483647.0
)。
请参阅如何使用 SSE/AVX 高效执行 double/int64 转换?对于相关的 double->uint64_t 转换。如果您需要,全范围的应该可以从 double->uint64_t 适应到 float->uint32_t。
另一种可能性(对于 32 位 float->uint32_t)只是范围移动到有符号的 FP,然后以整数形式返回。 INT32_MIN ^ convert(x + INT32_MIN)
. 但这引入了小整数的 FP 舍入,因为 INT32_MIN 在 -2 24 .. 2 24范围之外,其中 afloat
可以表示每个整数。eg5
将在转换期间四舍五入到最接近的 2 8倍数。所以那是不可用的;您需要尝试直接转换和范围移位转换,并且只有在直接转换给您的情况下才使用范围移位转换0x80000000
。(也许使用直接转换结果作为 SSE4 的混合控制blendvps
?)
对于 float->int32_t 的打包转换,有 SSE2 cvtps2dq xmm, xmm/m128
docs。(cvttps2dq
用截断向 0 转换,而不是当前的默认舍入模式(最近的,如果你没有改变它)。)
任何小于 -0.5 的负浮点数将转换为整数 -1 或更低;因为uint32_t
那个位模式代表了一个巨大的数字。超出 -2 31 ..2 31 -1 范围的浮点数将转换为0x80000000
Intel 的“整数不定”值。
如果你没有找到,只有 cvtps2pi 签名转换为 MMX 寄存器,你需要更好的地方来搜索:
cvtps2dq
and这样的相对常见/简单的指令cvttps2dq
。