Эта ошибка может не отображаться на всех машинах, но на моем я запустил приведенный ниже код и получил вывод (обратите внимание на значение -1.#IND00
)?
values int:: 4 2
shifts:: 4 2
result: : 64 32
input 1 HADDPD:: 10.000000 -1.#IND00
input 2 HADDPD:: 13.000000 10.000000
result of HADDPD:: -1.#IND00 23.000000
Если я закомментирую
__m64 PSLLDm64_IN = _mm_set_pi32(2,4);
__m64 PSLLDm64_C = _mm_set_pi32(2,4);//could this be the culprit?
__m64 PSLLDm64_r = PSLLD(PSLLDm64_IN, PSLLDm64_C);
print_2_32_bit_int("values int:" , PSLLDm64_IN);
print_2_32_bit_int("shifts:", PSLLDm64_C);
print_2_32_bit_int("result: ", PSLLDm64_r);
Я получил…
input 1 HADDPD:: 10.000000 100.000000
input 2 HADDPD:: 13.000000 10.000000
result of HADDPD:: 110.000000 23.000000
Мне интересно, если строка 32, где __m64 PSLLDm64_C = _mm_set_pi32(2,4);
можно облажаться?
Вот полный код (он работает с -msse3 -mmmx
используя g ++) не все заголовки действительно необходимы.
#include <xmmintrin.h>
#include <emmintrin.h>
#include <pmmintrin.h>
#include <stdio.h>
#include <stdint.h>
#include <iostream>
void print_2_64_bit_doubles(const char * label, __m128d m64_r)
{
double *val = (double *) &m64_r;
printf("%s: %f %f\n",
label, val[0], val[1]);
}
void print_2_32_bit_int(const char * label, __m64 m32_r)
{
int *val = (int *) &m32_r;
printf("%s: %d %d\n",
label, val[0], val[1]);
}
__m128d HADDPD(__m128d __X, __m128d __Y)
{
return _mm_hadd_pd ( __X, __Y);
}
__m64 PSLLD(__m64 __m, __m64 __count)
{
return _mm_sll_pi32 ( __m, __count);
}
int main()
{
//PSLLD-------------------------------------------------------------------
__m64 PSLLDm64_IN = _mm_set_pi32(2,4);
__m64 PSLLDm64_C = _mm_set_pi32(2,4);
__m64 PSLLDm64_r = PSLLD(PSLLDm64_IN, PSLLDm64_C);
print_2_32_bit_int("values int:" , PSLLDm64_IN);
print_2_32_bit_int("shifts:", PSLLDm64_C);
print_2_32_bit_int("result: ", PSLLDm64_r);
//HADDPD------------------------------------------------------------------
double C1 = 10;
double D = C1*C1;
double x = 10;
double y = 13;
__m128d HADDPDm64_1 = _mm_set_pd(D,C1);
__m128d HADDPDm64_2 = _mm_set_pd(x,y);
__m128d HADDPDm64_r = HADDPD( HADDPDm64_1, HADDPDm64_2);
print_2_64_bit_doubles("input 1 HADDPD:", HADDPDm64_1);
print_2_64_bit_doubles("input 2 HADDPD:", HADDPDm64_2);
print_2_64_bit_doubles("result of HADDPD:", HADDPDm64_r);
return 0;
}
РЕДАКТИРОВАТЬ: Это обновленный код с новыми инструкциями по смещению, скомпилированными с g ++ 4.4.1 -msse -msse2 -msse3 -msse4
#include <xmmintrin.h>
#include <emmintrin.h>
#include <pmmintrin.h>
#include <mmintrin.h>
#include <stdio.h>
#include <stdint.h>void print_2_64_bit_doubles(const char * label, __m128d m64_r)
{
double *val = (double *) &m64_r;
printf("%s: %f %f\n",
label, val[0], val[1]);
}
void print_2_32_bit_int(const char * label, __m64 m32_r)
{
int *val = (int *) &m32_r;
printf("%s: %d %d\n",
label, val[0], val[1]);
}
void print_1_32_bit_int(const char * label, __m64 m32_r)
{
int *val = (int *) &m32_r;
printf("%s: %d \n",
label, val[0]);
}
__m128d HADDPD(__m128d __X, __m128d __Y)
{
return _mm_hadd_pd ( __X, __Y);
}
__m64 PSLLD(__m64 __m, __m64 __count)
{
return _mm_sll_pi32 ( __m, __count);
}
int main()
{
//PSLLD-------------------------------------------------------------------
__m64 PSLLDm64_IN = _mm_set_pi32(2,4);
long long __i = 2;
__m64 PSLLDm64_C = (__m64)(__i);
__m64 PSLLDm64_r = PSLLD(PSLLDm64_IN, PSLLDm64_C);
_mm_empty();
print_2_32_bit_int("values int:" , PSLLDm64_IN);
print_1_32_bit_int("shifts:", PSLLDm64_C);
print_2_32_bit_int("result: ", PSLLDm64_r);
//HADDPD------------------------------------------------------------------
double C1 = 10;
double D = C1*C1;
double x = 10;
double y = 13;
__m128d HADDPDm64_1 = _mm_set_pd(D,C1);
__m128d HADDPDm64_2 = _mm_set_pd(x,y);
__m128d HADDPDm64_r = HADDPD( HADDPDm64_1, HADDPDm64_2);
print_2_64_bit_doubles("input 1 HADDPD:", HADDPDm64_1);
print_2_64_bit_doubles("input 2 HADDPD:", HADDPDm64_2);
print_2_64_bit_doubles("result of HADDPD:", HADDPDm64_r);
return 0;
}
И выход
values int:: 4 2
shifts:: 2
result: : 16 8
input 1 HADDPD:: 10.000000 -1.#IND00
input 2 HADDPD:: 13.000000 10.000000
result of HADDPD:: -1.#IND00 23.000000
Тестирование с портами Windows x64 gcc и g ++ 4.8.1 из http://www.drangon.org/mingw/ оба дают ожидаемые результаты. Просто распакуйте архив и установите путь к mingw64 \ bin. Используйте опцию компилятора, такую как -msse4, чтобы сообщить компилятору, что ваше оборудование поддерживает эти инструкции.
05.07.2013: Извините за неполный начальный комментарий. Кроме того, приведенный выше ответ был задуман как комментарий, а не как ответ.
Microsoft VS2010 получает тот же неверный результат, что вы сообщаете в cygwin, и причину легко найти с помощью отладчика Microsoft. На самом деле, предупреждение о компиляции также указывает на проблему:
warning C4730: 'main' : mixing _m64 and floating point expressions may result in incorrect code
Проблема, о которой вы сообщаете, возникает, когда компилятор генерирует набор инструкций MMX и x87 FPU. Компиляторы используют регистры MMX для данных _m64, а компиляторы используют либо регистры x87 FPU, либо более новые регистры XMM или YMM для типа данных с плавающей запятой double. Когда Intel разработала MMX, было принято решение повторно использовать регистры x87 для данных регистров MMX. Это было сделано для того, чтобы операционные системы не нуждались в каких-либо обновлениях для поддержки использования MMX. Недостаток этого решения заключается в том, что инструкции MMX и x87 FPU нельзя смешивать. Чтобы предотвратить случайное смешивание команд FPU и MMX, загруженные регистры MMX Intel помечают биты слова тега соответствующего регистра FPU как SNAN (сигнализируя NAN). Вот что приводит к неожиданному результату, который вы видите. Некоторые комбинации параметров компилятора и сборки могут позволить этому коду функционировать правильно. Возможные причины, по которым этот код может работать в некоторых случаях:
1) компилятор использует регистры XMM или YMM для данных с двойной точностью.
2) компилятор хранит все значения x87 FPU в памяти и не полагается на состояние регистра FPU в инструкциях MMX.
Суть в том, что кодер должен избегать ситуаций, которые позволяют компилятору генерировать код, который смешивает инструкции MMX и x87 FPU. Принимайте предупреждения, такие как «функция« print_2_32_bit_int »не имеет инструкции EMMS» или «смешивание _m64 и выражений с плавающей запятой может привести к неправильному коду». Один из подходов, который может быть осуществимым, состоит в том, чтобы вообще избежать типа данных _m64.
Предложение Пола Р. об использовании _mm_empty () решает проблему для Microsoft VS2010. Я добавил его до ‘double C1 = 10’, и проблема ушла. _mm_empty объясняется здесь http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011Update/compiler_c/intref_cls/common/intref_mmx_emms_usage.htm.
По другим вопросам я использую командную строку только для gcc, без IDE. Старая версия gcc должна работать нормально, если вы добавляете _mm_empty () или избегаете смешивания кодов MMX и x87 FPU.
Других решений пока нет …