当使用诸如PADDD
之类的SSE2指令(即_mm_add_epi32
内在函数)时,有没有办法检查是否有任何操作溢出?
我认为MXCSR控制寄存器上的标志可能会在溢出后设置,但我看不到这种情况。例如,_mm_getcsr()
在以下两种情况下(8064)打印相同的值:
#include <iostream>
#include <emmintrin.h>
using namespace std;
void main()
{
__m128i a = _mm_set_epi32(1, 0, 0, 0);
__m128i b = _mm_add_epi32(a, a);
cout << "MXCSR: " << _mm_getcsr() << endl;
cout << "Result: " << b.m128i_i32[3] << endl;
__m128i c = _mm_set_epi32((1<<31)-1, 3, 2, 1);
__m128i d = _mm_add_epi32(c, c);
cout << "MXCSR: " << _mm_getcsr() << endl;
cout << "Result: " << d.m128i_i32[3] << endl;
}
还有其他方法可以检查SSE2的溢出吗?
答案 0 :(得分:10)
以下是@hirschhornsalz's sum_and_overflow
function的更高效版本:
void sum_and_overflow(__v4si a, __v4si b, __v4si& sum, __v4si& overflow)
{
__v4si sa, sb;
sum = _mm_add_epi32(a, b); // calculate sum
sa = _mm_xor_si128(sum, a); // compare sign of sum with sign of a
sb = _mm_xor_si128(sum, b); // compare sign of sum with sign of b
overflow = _mm_and_si128(sa, sb); // get overflow in sign bit
overflow = _mm_srai_epi32(overflow, 31); // convert to SIMD boolean (-1 == TRUE, 0 == FALSE)
}
它使用表达式来自Hacker's Delight第27页的溢出检测:
sum = a + b;
overflow = (sum ^ a) & (sum ^ b); // overflow flag in sign bit
请注意,对于TRUE(溢出),溢出向量将包含更常规的SIMD布尔值-1,对于FALSE(无溢出),将包含0。如果你只需要在符号位中溢出而其他位是&#34;请不要关心&#34;然后你可以省略函数的最后一行,将SIMD指令的数量从5减少到4。
注意:此解决方案以及previous solution on which it is based用于有符号整数值。无符号值的解决方案需要稍微不同的方法(请参阅@Stephen Canon&#39; s answer)。
答案 1 :(得分:9)
由于你有4个可能的溢出,控制寄存器会非常快地耗尽位,特别是,如果你想要carrys,sign等等,即使是由16个字节组成的向量加法:-)
< / p>
如果输入符号位相等且结果符号位与输入符号位不同,则设置溢出标志。
此函数计算sum = a+b
并手动溢出。对于每个溢出,0x80000000都是overflow
中的回退。
void sum_and_overflow(__v4si a, __v4si b, __v4si& sum, __v4si& overflow) {
__v4si signmask = _mm_set1_epi32(0x80000000);
sum = a+b;
a &= signmask;
b &= signmask;
overflow = sum & signmask;
overflow = ~(a^b) & (overflow^a); // overflow is 1 if (a==b) and (resultbit has changed)
}
注意:如果您没有gcc,则必须使用相应的SSE内在函数替换^
&
+
运算符,例如_mm_and_si128()
,{{1等等。
编辑:我刚注意到带有掩码的_mm_add_epi32()
当然可以在函数的最后完成,节省了两个and
操作。但编译器很可能足够聪明,可以单独完成。
答案 2 :(得分:5)
我注意到你也要求一个无符号的解决方案;幸运的是,这也很容易:
__v4si mask = _mm_set1_epi32(0x80000000);
sum = _mm_add_epi32(a, b);
overflow = _mm_cmpgt_epi32(_mm_xor_si128(mask, a), _mm_xor_si128(mask, sum));
通常要检测无符号溢出,只需选中sum < a
或sum < b
即可。但是,SSE没有未签名的比较; xor
- 使用0x80000000
的参数可以使用签名的比较来获得相同的结果。
答案 3 :(得分:2)