c++ - traduccion - integer overflow
ComprobaciĆ³n de desbordamiento de enteros SSE2 (4)
Cuando se usan instrucciones SSE2 como PADDD
(es decir, _mm_add_epi32
intrínseco), ¿hay alguna forma de verificar si alguna de las operaciones se desbordó?
Pensé que tal vez una bandera en el registro de control MXCSR podría establecerse después de un desbordamiento, pero no veo que eso suceda. Por ejemplo, _mm_getcsr()
imprime el mismo valor en los dos casos a continuación (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;
}
¿Hay alguna otra forma de verificar el desbordamiento con SSE2?
Aquí hay una versión algo más eficiente de @hirschhornsalz''s función sum_and_overflow
de @hirschhornsalz''s :
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)
}
Utiliza una expresión para la detección de desbordamiento de la página 27 de Hacker''s Delight :
sum = a + b;
overflow = (sum ^ a) & (sum ^ b); // overflow flag in sign bit
Tenga en cuenta que el vector de desbordamiento contendrá los valores booleanos SIMD más convencionales de -1 para VERDADERO (desbordamiento) y 0 para FALSO (sin desbordamiento). Si solo necesita el desbordamiento en el bit de signo y los otros bits son "no importa", puede omitir la última línea de la función, reduciendo el número de instrucciones SIMD de 5 a 4.
NB: esta solución, así como la solución anterior en la que se basa, son para valores enteros con signo. Una solución para valores sin firma requerirá un enfoque ligeramente diferente (consulte la answer @Stephen Canon ).
Debido a que tiene 4 posibles desbordamientos, el registro de control se quedaría rápidamente sin bits, especialmente, si quisiera acarrear, firmar, etc. y eso incluso para una adición de vector que consta de 16 bytes :-)
El indicador de desbordamiento se establece, si los bits de signo de entrada son iguales y el bit de signo de resultado es diferente de un bit de signo de entrada.
Esta función calcula sum = a+b
y desbordamiento manualmente. Por cada desbordamiento, 0x80000000 es recurrente en 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)
}
Nota: Si no tiene gcc, debe reemplazar los operadores ^
&
+
por los intrínsecos SSE apropiados, como _mm_and_si128()
, _mm_add_epi32()
etc.
Edición: Me acabo de dar cuenta de que, con la máscara, se puede hacer al final de la función, guardando dos and
operaciones. Pero es muy probable que el compilador sea lo suficientemente inteligente como para hacerlo solo.
Me doy cuenta de que también pediste una solución para los no firmados; Afortunadamente, eso también es bastante fácil:
__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));
Normalmente, para detectar un desbordamiento no firmado, simplemente marque la sum < a
o la sum < b
. Sin embargo, SSE no tiene comparaciones sin firmar; xor
los argumentos con 0x80000000
puede usar una comparación firmada para obtener el mismo resultado.