AVX/SSE 轮向下浮动并返回整数向量?

mez*_*hic 5 c++ sse intel intrinsics avx

有没有办法使用 AVX/SSE 来获取浮点数向量,向下取整并生成整数向量?所有的 floor 内在方法似乎都会产生一个最终的浮点向量,这很奇怪,因为四舍五入会产生一个整数!

zx4*_*485 5

SSE 可以根据您选择的截断(向零)或当前舍入模式(通常是 IEEE 默认模式,最接近的平局舍入到偶数)从​​ FP 转换为整数。类似于 ,与nearbyint()平局round()远离 0 的情况不同。如果您在 x86 上需要这种舍入模式,您必须模拟它,也许使用 truncate 作为构建块。)

\n\n

相关指令是CVTPS2DQ和CVTTPS2DQ,用于将压缩单精度转换float为有符号双字整数。助记符中带有额外 T 的版本执行截断而不是当前的舍入模式。

\n\n
; xmm0 is assumed to be packed float input vector\ncvttps2dq xmm0, xmm0\n; xmm0 now contains the (rounded) packed integer vector\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者使用内在函数,__m128i _mm_cvt[t]ps_epi32(__m128 a)

\n\n
\n\n

对于 x86 在硬件中提供的其他两种舍入模式,floor(朝 -Inf)和 ceil(朝 +Inf),一种简单的方法是在转换为整数之前使用此 SSE4.1/AVX ROUNDPS指令。

\n\n

代码如下所示:

\n\n
roundps  xmm0, xmm0, 1    ; nearest=0, floor=1,  ceil=2, trunc=3\ncvtps2dq xmm0, xmm0       ; or cvttps2dq, doesn\'t matter\n; xmm0 now contains the floored packed integer vector\n
Run Code Online (Sandbox Code Playgroud)\n\n

对于 AVX ymm 向量,请在指令前添加“V”并将 xmm 更改为 ymm。

\n\n
\n\n

ROUNDPS 的工作原理如下

\n\n
\n

对 xmm2/m128 中的压缩单精度浮点值进行四舍五入,并将结果放入 xmm1 中。舍入模式由imm8决定。

\n
\n\n

舍入模式(立即数/第三个操作数)可以具有以下值(取自4-15 - Rounding Modes and Encoding of Rounding Control (RC) Field当前英特尔文档的表):

\n\n
Rounding Mode               RC Field Setting   Description\n----------------------------------------------------------\nRound to nearest (even)     00B                Rounded result is the closest to the infinitely precise result. If two values are equally close, the result is nearest (even) the even value (i.e., the integer value with the least-significant bit of zero).\nRound down (toward \xe2\x88\x92\xe2\x88\x9e)      01B                Rounded result is closest to but no greater than the infinitely precise result.\nRound up (toward +\xe2\x88\x9e)        10B                Rounded result is closest to but no less than the infinitely precise result.\nRound toward 0 (truncate)   11B                Rounded result is closest to but no greater in absolute value than the infinitely precise result.\n
Run Code Online (Sandbox Code Playgroud)\n\n

舍入运算的返回向量是float或不是的可能原因可能是,通过这种方式,进一步的运算始终可以是浮点运算(对舍入值),并且如图所示,int到 的转换将是微不足道的。int

\n\n

相应的内在函数可以在引用的文档中找到。将上述代码转换为内在函数(取决于Rounding Control (RC) Field)的示例是:

\n\n
__m128 dst = _mm_cvtps_epi32( _mm_floor_ps(__m128 src) );\n
Run Code Online (Sandbox Code Playgroud)\n

  • 仅当您需要向 -Inf(下限)或 +Inf(上限)舍入时才需要“roundps”。`cvtps_epi32` 使用默认舍入模式(通常与 `roundps 00` 相同),而 `cvttps_epi32` 则向 0 截断。 (4认同)
  • “roundps”不转换为“int”的一个主要原因是“FLT_MAX”大于“INT_MAX”,因此结果可能无法表示(转换超出范围的 FP 值会得到“0x80000000”,“英特尔称之为“整数不定值”)。另外,有时即使不将 FP 数字转换为整数,对它们进行舍入也是非常有用的。 (3认同)

dou*_*536 1

使用转换指令:


int _mm_cvt_ss2si (__m128 a)
Run Code Online (Sandbox Code Playgroud)

将 的低 32 位浮点部分转换a为整数并返回该整数。的上面三个组成部分a将被忽略。


__m128i _mm_cvtps_epi32 (__m128 a);
Run Code Online (Sandbox Code Playgroud)

将所有 4 个 32 位浮点数转换为整数,并返回 4 个 32 位整数的向量。


这些是经常使用的。还有其他变体可以处理转换。

  • OP 想要一个向下舍入的结果。假设他的意思是朝向 0,而不是朝向 -Inf,则“_mm_cvttps_epi32”是理想指令的内在特征。要向 -Inf 舍入,您需要“roundps”,或者临时更改默认舍入模式。 (4认同)
  • @mezamorphic:如果你有“-4.9”,你想要“-5”(下限)还是“-4”(截断)? (2认同)
  • @mezamorphic:在这种情况下,Peter Cordes 提供的解决方案将是最合适的:使用 `_mm_cvttps_epi32`/`CVTTPS2DQ`。 (2认同)