_mm_xor_pd не работает с product = 0 - PullRequest
0 голосов
/ 13 февраля 2020

Я начинаю учиться переходить некоторый код, который я работаю в целочисленной математике, чтобы использовать более быструю операцию с плавающей запятой в графическом процессоре. Я не могу понять, почему я не могу заставить работать _mm_xor_pd (). Только для целей тестирования я ввел одно и то же значение в оба вектора следующим образом:

__m128d shift04 = _mm_set1_pd(16);

__m128d v1 = _mm_set1_pd(0x33F4A032);

__m128d k0 = _mm_set1_pd(0x6A6BA9EF);

__m128d j = _mm_add_pd(_mm_mul_pd(v1,shift04),k0); //  j = (v1 << 4) + k0

__m128d k = _mm_add_pd(v1,sum);                    //  k = v1 + sum

__m128d l = _mm_xor_pd(j,k);                       //  l = j ^ k

Я понимаю, что приведенный выше код ужасен, но это только для базового c тестирования, поэтому, пожалуйста, будьте терпеливы со мной. Значение l[0] и l[1] равно 0?

j[0] правильно равно 0xa9b5ad0f

k[0] правильно равно 0xd22c19eb

l[0] должно равняться 0x7b99b4e4, но приведенный выше код дает 0x0000000 .

В настоящее время я предполагаю, что это потому, что вы не можете нормально XOR с плавающей запятой, но функция ясно подсказывает, что она делает. Я не прав?

Ubuntu 19.1, G ++ версии 9.2.1, Radeon VII и Intel I7 2600k.

Ответы [ 2 ]

2 голосов
/ 13 февраля 2020

Вы можете XOR чисел с плавающей запятой с _mm_xor_pd, но это действительно XOR чисел с плавающей запятой , он не преобразует числа с плавающей точкой в ​​целые числа и XOR тех. Плавания сложнее целых чисел, например, умножение на 16 не сдвигает биты, оно увеличивает показатель степени на 4 и оставляет другие биты на месте (за исключением ненормальных чисел или когда переполнение показателя или некоторые другие крайние случаи).

Результат здесь, вероятно, не нулевой (я не могу проверить это, потому что sum не определено), но крошечный, так что преобразование его в целое число приводит к нулю.

переведите некоторый код, который у меня работает, в целочисленную математику, чтобы использовать более быструю операцию с плавающей запятой графического процессора

К счастью, также существует целочисленное SIMD, так что вы можете просто использовать его и не бороться с плавающими числами.

Например:

__m128i v1 = _mm_set1_epi32(0x33F4A032);
__m128i k0 = _mm_set1_epi32(0x6A6BA9EF);
__m128i j = _mm_add_epi32(_mm_slli_epi32(v1, 4), k0); //  j = (v1 << 4) + k0
__m128i k = _mm_add_epi32(v1, sum);                   //  k = v1 + sum
__m128i l = _mm_xor_si128(j, k);                      //  l = j ^ k

Имейте в виду, что главным моментом SIMD является векторизация, не очень полезно использовать его с всегда одинаковыми значениями в каждой «полосе», это просто тратить 3/4 потенциала.

0 голосов
/ 13 февраля 2020

Вы сохраняете биты двойников, а не целочисленные значения, которые вы сохранили в двойниках. Если вы распечатаете биты, вы увидите, что вы получаете правильное значение (которое не 0, а очень маленькое число: 7e-290):

#include <iostream>
#include <emmintrin.h>

int main()
{
    __m128d sum = _mm_set1_pd(0x9E3779B9);
    __m128d shift04 = _mm_set1_pd(16);
    __m128d v1 = _mm_set1_pd(0x33F4A032);
    __m128d k0 = _mm_set1_pd(0x6A6BA9EF);
    __m128d j = _mm_add_pd(_mm_mul_pd(v1,shift04),k0); //  j = (v1 << 4) + k0
    __m128d k = _mm_add_pd(v1,sum);                    //  k = v1 + sum
    __m128d l = _mm_xor_pd(j,k);
    std::cout << l[0] << "\n";
    std::cout << std::hex << *reinterpret_cast<int64_t*>(&j[0]) << "^" << *reinterpret_cast<int64_t*>(&k[0]) << " = " << *reinterpret_cast<int64_t*>(&l[0]) << "\n";  
}

Примечание. Не используйте приведенный выше код для чего-либо важного доступ к элементам __m128d является неопределенным поведением, как и reinterpret_cast передача их в int64_t.

Программа выводит:

7.38559e-290
420d4dad68780000^41ea45833d600000 = 3e7082e55180000

0x420d4dad68780000 ^ 0x41ea45833d600000 is 0x3e7082e55180000

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...