如何获得英特尔架构 SIMD __m128 的标志
Posted
技术标签:
【中文标题】如何获得英特尔架构 SIMD __m128 的标志【英文标题】:How do I get the sign of an intel Architecture SIMD __m128 【发布时间】:2018-01-21 02:53:33 【问题描述】:据我所知,“_mm_sign_ps”不存在:给定一个具有四个浮点值的 __m128 值,SIMD 指令或 SIMD 指令列表会将其转换为具有四个浮点值的 __m128 值点值包含:
+1,如果四个的原始值为正且大于零。 0,如果四个的原始值为零。 -1,如果四个的原始值为负数且小于零。
【问题讨论】:
【参考方案1】:SSE 确实与此完全不匹配。首先,比较函数不会导致±1.0f,而是如果条件为真则设置所有位,或者如果条件为假则不设置任何位。此外,您要求进行三向比较,如果值为“零”,则结果为“零”(“零”在引号中,因为您实际上并未指定您想要正零还是负零;IEEE 754两者都有)。如果您可以重新定义问题以更好地匹配 SSE 提供的内容,您的情况会好多。
也就是说:
__m128 foo (__m128 value)
const __m128 zero = _mm_set_ps1 (0.0f);
__m128 positives = _mm_and_ps(_mm_cmpgt_ps (value, zero), _mm_set_ps1(1.0f));
__m128 negatives = _mm_and_ps(_mm_cmplt_ps (value, zero), _mm_set_ps1(-1.0f));
return _mm_or_ps(positives, negatives);
我不知道你打算用它来做什么,但如果你对按位运算感到满意,那么你很有可能会弄清楚如何使用单个 _mm_cmpgt_ps
、_mm_cmpge_ps
、_mm_cmplt_ps
或 _mm_cmple_ps
。
【讨论】:
为了将数轴划分为三个等价类,至少需要进行两次比较。 是的,这就是为什么我在答案中有两个比较。但是,通常没有必要以不同的方式对待零,因此,如果您小心处理问题的方式,则可以将所有内容简化为一个比较,甚至可能不需要将 0xffffffff 转换为 ± 1.0f. 我猜他们可能想乘以这个,(条件符号翻转或零),在这种情况下,它只需要几个布尔值和一个 cmpeq_ps。【参考方案2】:SSE 不会自然/有效地以这种方式用于浮点/双精度。你到底想用-1.0f
/ 0.0f
/ 1.0f
sgn(x) 值做什么?
您可能应该优化将这些 FP 值实际保存在寄存器中的步骤,并直接使用比较掩码结果。你问的问题是an X-Y problem 的标志。是的,您实际上可以实现这一点,但通常您不应该这样做。
例如,您可以使用布尔 AND 或 compare+AND 来获取符号位的掩码,然后可以使用布尔 XOR (_mm_xor_ps()
) 来翻转另一个设置了这些位的向量中的符号位,然后离开修改对应元素中未设置符号位的元素。
(FP 否定就像翻转符号位一样简单,因为 IEEE-754 二进制格式使用符号/大小表示。)
但要小心-0.0
,因为它设置了符号位。如果您想根据相应元素为零来将元素归零,并为其他元素翻转或不翻转,您可以使用几个布尔运算,然后用 _mm_cmpeq_ps
的结果对 0.0 进行屏蔽。 (这对于 0.0 和 -0.0 是正确的)。
例如:
// SSE2 v * sgn(src), except we treat src=NaN as src=0
__m128 mul_by_signum(__m128 v, __m128 src)
__m128 signbits = _mm_and_ps(src, _mm_set1_ps(-0.0)); // epi32(1<<31)
__m128 flipped = _mm_xor_ps(v, signbits);
__m128 nonzero = _mm_cmpne_ps(src, _mm_setzero_ps());
return _mm_and_ps(flipped, nonzero);
对于整数,有SSSE3 psignb/w/d
,它将根据源为正/零/负在目标中保留/零/否定元素。如果目的地为_mm_set1_epi32(1)
,它将为您提供一个包含 1/0/-1 个元素的向量。
您不能对 FP 数据有用地使用它,因为 FP 使用符号/大小而不是 2 的补码。而且因为它检查整数零,所以-0.0
看起来像一个负数。
顺便说一句,您没有提到您希望 NaN FP 输入发生什么。不要忘记 FP 比较有 4 种可能的结果:高于/等于/低于,或者如果一个或两个操作数都是 NaN,则无序。 (因此,为了与零进行比较,您可以使用正数、零、负数或 NaN)。
【讨论】:
不幸的是,您对 X-Y 问题的看法是正确的。我真正想做的是使用 SIMD 规范化一组 SOA 向量,其中一些从 [0, 0, 0] 开始,但没有任何 +/-Inf 或 NaN 结果结束。符号问题只是我想出的复杂解决方案的一部分。我应该编辑这个问题,还是创建一个新问题? @NarftheMouse:哦,这相对容易。nonzero_magnitude = _mm_cmpne_ps(sum_of_squares, 0.0);
,然后使用它来将产生 0 / 0 的元素的除法(或 rsqrtps
)结果掩码为 0.0。因此只需一个额外的 cmpps
和一个额外的 andps
即可将其中的元素归零输入产生 Inf 或 NaN。作为奖励,与早期的临时结果进行比较而不是检查最终结果是否为 NaN 会使比较脱离延迟关键路径。我认为以前有人问过这个问题(至少有一些关于一般想法的问题),但如果你找不到它,一定要问一个新问题。
好的,谢谢。我会在实施后回复您。以上是关于如何获得英特尔架构 SIMD __m128 的标志的主要内容,如果未能解决你的问题,请参考以下文章