Допустим, у вас есть 128-разрядное целое число без знака
28018020645823955151501786048551321856
В шестнадцатеричном виде это
0x15141312111009080706050403020100
На архитектурах, использующих порядок байтов с прямым порядком байтов , например 64-битный Intel / AMD (что является наиболее вероятным кандидатом, учитывая используемый тип __m128i
), это число сохраняется в памяти в шестнадцатеричный как
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15
Мы можем интерпретировать эти байты, например, как восемь 16-разрядных целых чисел без знака,
0x0100 0x0302 0x0504 0x0706 0x0908 0x1110 0x1312 0x1514
или четыре 32-разрядных целых числа без знака,
0x03020100 0x07060504 0x11100908 0x15141312
или два 64-разрядных целых числа без знака,
0x0706050403020100 0x1514131211100908
OP хочет разделить входные данные 128-разрядного целого без знака на два 64-разрядных целых числа без знака. Встроенные функции Intel / AMD предоставляют для этого _mm_shuffle_epi8()
и _mm_set_epi8()
. (Если OP использует TNS / X C / C ++, эквивалентные внутренние свойства _pshufb()
и _mm_set_epi8()
.)
Свойство _mm_set_epi8()
принимает 16 параметров, в первую очередь старший байт, и упаковывает их в 128-битное целое число. Встроенные _mm_shuffle_epi8()
/ _pshufb()
принимают два 128-разрядных целых числа в качестве параметров и возвращают 128-разрядное целое число, построенное из байтов в первом параметре, как указано байтами во втором параметре.
Вот несколько полезных констант порядка байтов:
/* SWAP128_128 = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); */
#define SWAP128_128 { 579005069656919567LL, 283686952306183LL }
/* SWAP128_64 = _mm_set_epi8(8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7); */
#define SWAP128_64 { 283686952306183LL, 579005069656919567LL };
/* SWAP128_32 = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3); */
#define SWAP128_32 { 289644378169868803LL, 868365760874482187LL };
/* SWAP128_16 = _mm_set_epi8(14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1); */
#define SWAP128_16 { 434320308619640833LL, 1013041691324254217LL };
const __m128i swap128_128 = SWAP128_128;
const __m128i swap128_64 = SWAP128_64;
const __m128i swap128_32 = SWAP128_32;
const __m128i swap128_16 = SWAP128_16;
Обратите внимание, что объявление констант предполагает, что компилятор C реализует тип __m128i
, как если бы это было два long long
s (насколько я знаю, все, что поддерживают те, что для SSE3, делают). В любом случае вы можете создать константы, используя _mm_set_epi8()
встроенную функцию.
Причина, по которой они помещаются в макросы, заключается в том, что если вы встретите компилятор или архитектуру , для которых требуется объявление другого типа, чтобы получить то же эффективное значение (что и для соответствующих внутренних значений _mm_set_epi8()
), вы нужен только небольшой массаж препроцессора.
Используя вышеизложенное, a = _mm_shuffle_epi8(a, swap128_128);
(или a = _pshufb(a, swap128_128)
для TNS / X C / C ++) полностью изменяет порядок байтов; swap128_64
только порядок байтов для обоих 64-разрядных компонентов, swap128_32
для всех четырех 32-разрядных компонентов и swap128_16
для всех восьми 16-разрядных компонентов. Существует одиннадцать других вариантов (плюс «без случайного выбора», всего 16 порядков байтов для 128-битных значений), плюс вы можете дублировать исходные байты для целевых байтов, поэтому используйте _mm_set_epi8()
, чтобы найти нужный.
Учитывая приведенные выше данные,
const uint8_t data[16] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15
};
__m128i vector = _mm_lddqu_si128((const __m128i *)data);
__m128i v128 = _mm_shuffle_epi8(vector, swap128_128);
__m128i v64 = _mm_shuffle_epi8(vector, swap128_64);
__m128i v32 = _mm_shuffle_epi8(vector, swap128_32);
__m128i v16 = _mm_shuffle_epi8(vector, swap128_16);
даст:
vector = 0x0706050403020100 0x1514131211100908
= 0x03020100 0x07060504 0x11100908 0x15141312
= 0x0100 0x0302 0x0504 0x0706 0x0908 0x1110 0x1312 0x1514
= 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15
v128 = 0x0809101112131415 0x0001020304050607
= 0x12131415 0x08091011 0x04050607 0x00010203
= 0x1415 0x1213 0x1011 0x0809 0x0607 0x0405 0x0203 0x0001
= 0x15 0x14 0x13 0x12 0x11 0x10 0x09 0x08 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00
v64 = 0x0001020304050607 0x0809101112131415
= 0x04050607 0x00010203 0x12131415 0x08091011
= 0x0607 0x0405 0x0203 0x0001 0x1415 0x1213 0x1011 0x0809
= 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00 0x15 0x14 0x13 0x12 0x11 0x10 0x09 0x08
v32 = 0x0405060700010203 0x1213141508091011
= 0x00010203 0x04050607 0x08091011 0x12131415
= 0x0203 0x0001 0x0607 0x0405 0x1011 0x0809 0x1415 0x1213
= 0x03 0x02 0x01 0x00 0x07 0x06 0x05 0x04 0x11 0x10 0x09 0x08 0x15 0x14 0x13 0x12
v16 = 0x0607040502030001 0x1415121310110809
= 0x02030001 0x06070405 0x10110809 0x14151213
= 0x0001 0x0203 0x0405 0x0607 0x0809 0x1011 0x1213 0x1415
= 0x01 0x00 0x03 0x02 0x05 0x04 0x07 0x06 0x09 0x08 0x11 0x10 0x13 0x12 0x15 0x14
в зависимости от того, как вы хотите интерпретировать каждый __m128i
. (Первое - это два 64-разрядных целых числа, второе - четыре 32-разрядных числа, третье - восемь 16-разрядных чисел и четвертое - шестнадцать байтов.)
Существует много других возможных вариантов (для 128-битных значений возможны 16 уникальных порядков байтов), но, не зная точно, в чем заключается основная проблема и что именно пытается достичь OP, я не буду беспокоиться об их изучении все.