【问题标题】:AVX/SSE round floats down and return vector of ints?AVX/SSE 轮向下浮动并返回整数向量?
【发布时间】:2016-09-02 15:37:40
【问题描述】:

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

【问题讨论】:

  • 转换为整数必须以某种方式舍入或截断,但您可以将 FP 值舍入为最接近的整数而无需转换。 (见float nearbyintf(float x)
  • round() 的定义 - stackoverflow.com/questions/3597197
  • @Royi:你能删除你的误导性评论吗? C round() 舍入模式没有 x86 内在函数,它从零舍入 +-0.5。使用最近舍入的 x86 舍入内在函数使用 IEEE 默认舍入模式,银行家舍入(最近甚至作为平局)rint / lrint / nearbyint 都为您提供默认舍入模式,并且速度更快比 x86 上的 round() (尤其是使用 SSE4.1 或 AVX,或者您转换为整数的任何时间)。所以链接到round() 的定义是题外话。
  • @PeterCordes,我链接到定义round() 的答案,就像在维基百科中一样。我没有得到你的评论,C 中的round() 是否遵守这个定义?
  • 这个问题一般是关于四舍五入的,没有 C round() 函数。特别是四舍五入 down (地板或截断)。这个问题没有说明使用 C round() 函数的舍入模式的舍入函数。这不是默认的 IEEE 舍入模式或任何东西。如果您要讨论其他舍入模式,在答案中提及它作为_mm_round_ps(x, _MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC) 的替代品可能会很有趣。

标签: c++ intel sse intrinsics avx


【解决方案1】:

SSE 可以从 FP 转换为整数,您可以选择截断(接近零)或当前舍入模式(通常是 IEEE 默认模式,最接近 tiebreaks 舍入为偶数。像 nearbyint(),不像 round() 中的 tiebreak远离 0。如果您需要 x86 上的舍入模式,you have to emulate it, perhaps with truncate as a building block。)

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

; xmm0 is assumed to be packed float input vector
cvttps2dq xmm0, xmm0
; xmm0 now contains the (rounded) packed integer vector

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


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

代码如下所示:

roundps  xmm0, xmm0, 1    ; nearest=0, floor=1,  ceil=2, trunc=3
cvtps2dq xmm0, xmm0       ; or cvttps2dq, doesn't matter
; xmm0 now contains the floored packed integer vector

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


ROUNDPS 是这样工作的

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

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

Rounding Mode               RC Field Setting   Description
----------------------------------------------------------
Round 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).
Round down (toward −∞)      01B                Rounded result is closest to but no greater than the infinitely precise result.
Round up (toward +∞)        10B                Rounded result is closest to but no less than the infinitely precise result.
Round toward 0 (truncate)   11B                Rounded result is closest to but no greater in absolute value than the infinitely precise result.

舍入运算的返回向量是float 而不是int 的可能原因可能是,通过这种方式,进一步的运算总是可以是浮点运算(在舍入值上)并且转换为int 会如图所示。

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

__m128 dst = _mm_cvtps_epi32( _mm_floor_ps(__m128 src) );

【讨论】:

  • roundps 仅在您需要向 -Inf(地板)或 +Inf(天花板)四舍五入时才需要。 cvtps_epi32 使用默认的舍入模式(通常与roundps 00 相同),而cvttps_epi32 向0 截断。
  • roundps 不转换为int 的一个主要原因是FLT_MAX 大于INT_MAX,因此结果可能无法表示(您从转换中得到0x80000000-范围内的 FP 值,英特尔称之为“整数不定”值)。此外,有时即使不将其转换为整数,也可以将 FP 数字四舍五入。
  • roundps 的一个更好的例子是 floor 或 ceil 模式,默认舍入模式下的 cvt 或 cvtt 指令无法实现。截断然后转换为最近应该只用一条 cvtt 指令完成,所以它不会显示 roundps 在这种情况下有什么用处。
【解决方案2】:

使用转换说明:


int _mm_cvt_ss2si (__m128 a)

a 的低32 位浮点组件转换为整数并返回该整数。 a 的前三个组件被忽略。


__m128i _mm_cvtps_epi32 (__m128 a);

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


这些是常用的。有additional variations处理转化。

【讨论】:

  • OP 想要一个四舍五入的结果。假设他的意思是指向 0,而不是指向 -Inf,_mm_cvttps_epi32 是理想指令的内在属性。要向 -Inf 舍入,您需要 roundps,或临时更改默认舍入模式。
  • @PeterCordes 我不确定你的意思,但如果我有 4.9,我想要 4,如果我有 100.3434,我想要 100?
  • @mezamorphic:如果你有-4.9,你想要-5(地板)还是-4(截断)?
  • 我们可以假设我不会有负数。速度非常重要(如果有帮助的话)。
  • @mezamorphic:在这种情况下,Peter Cordes 提供的解决方案将是最合适的:使用_mm_cvttps_epi32/CVTTPS2DQ
【解决方案3】:

单指令选项:

  • 向零截断:__m128i _mm_cvttps_epi32(__m128 a)
  • 四舍五入:__m128i _mm_cvtps_epi32(__m128 a)

两条指令,使用SSE4.1 ROUNDPS,然后使用cvtps_epi32

  • -INF 转:__m128 _mm_floor_ps(__m128 s1)
  • 转向+INF: __m128 _mm_ceil_ps(__m128 s1)

如果您想以 FP 格式保存数据,请仅使用 roundps 的其他截断或最接近的形式。


对于正数,截断和下限是相同的。对于负整数,cvtt(-4.9) = -4,但floor(-4.9) = -5.0。参见floorf()truncf()


如果 FP 值超出 INT_MININT_MAX 的范围,cvttps and cvtps will give you 0x80000000(即INT_MIN,只是符号位集),英特尔将其称为“整数不定”值。它也会引发 FP 无效异常,但 FP 异常默认被屏蔽。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2021-03-25
  • 1970-01-01
  • 1970-01-01
  • 2016-01-04
  • 2013-07-19
  • 1970-01-01
  • 2015-05-15
  • 2016-01-11
相关资源
最近更新 更多