SSE内置函数位右移

3

我正在尝试使用内置函数将整数右移。下面的代码尝试这样做,但输出结果看起来并不如预期,可能是我错误地加载了数字或使用了错误的内置函数。以下是输出结果:

 2 4 8 16 32 64 128 1 2 4 8 16 32 64 128 0
 512 1024 2048 4096 8192 16384 32768 0
 0 8192 0 16384
 8 0 16 0

我曾尝试查看这个线程,但它甚至没有尝试使用带有SSE内在函数的位移指令

这是完整的代码(使用SSE2标志编译)。

#include <emmintrin.h>
#include <stdio.h>
#include <stdint.h>

void print_16_num(__m128i var)
{
    uint8_t *val = (uint8_t*) &var;
    printf(" %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i \n",
           val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7],val[8], val[9], val[10], val[11], val[12], val[13], val[14], val[15]);
}
void print_8_num( __m128i var)
{
    uint16_t *val = (uint16_t*) &var;
    printf(" %i %i %i %i %i %i %i %i \n",
           val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
}
void print_4_num( __m128i var)
{
    uint16_t *val = (uint16_t*) &var;
    printf(" %i %i %i %i \n",
           val[0], val[1], val[2], val[3]);
}
int main()
{
    __m128i _16 = _mm_set_epi8( 128, 64, 32, 16, 8, 4, 2, 1, 128, 64, 32, 16, 8, 4, 2, 1);
    print_16_num(_mm_srli_si128(_16,1));

   __m128i _8 = _mm_set_epi16( 128, 64, 32, 16, 8, 4, 2, 1);
    print_8_num( _mm_srli_si128(_8,1));

    __m128i _4 = _mm_set_epi32( 128, 64, 32, 16);
    print_4_num( _mm_srli_si128(_4,1));

    _4 = _mm_set_epi32( 128, 64, 32, 16);
    print_4_num( _mm_srli_epi32(_4,1));

    return 0;
}

根据您所引用的文档,_mm_srli_si128按字节移位,而不是位 - Raymond Chen
@RaymondChen 哦,难怪它不起作用。所以我猜链接(到其他stackoverflow问题)是移位的唯一方式? - pandoragami
据我所知,只有移动128位值才会移动字节,而64位、32位和16位则使用位。我猜设计者们质疑你为什么要移动这样一个大的值。 - mukunda
1个回答

4
当您使用_mm_set_epi*函数时,它们会将参数作为最高有效位优先接受。
例如,第一条语句, __m128i _16 = _mm_set_epi8( 128, 64, 32, 16, 8, 4, 2, 1, 128, 64, 32, 16, 8, 4, 2, 1); 会将变量加载为以下值:
0x80402010080402018040201008040201
 (128,64,32 ...)

然后使用_mm_srli_si128(_16,1)将这个128位的值向右移动1个字节,你会得到

0x00804020100804020180402010080402

当您阅读单个字节值时,byte [0] 是最不重要的字节,也就是最右边的那个字节。(因此它打印02 04 08等等...)。其他语句也是同样的道理,尽管我认为您想在print_4_num函数内强制转换为uint32_t*而不是uint16_t*。对于最后一个,_mm_srli_epi32(_4,1)将移动该值。
0x00000080000000400000002000000010
       (128)   (64)    (32)    (16)

只向右位移一次,就会变成

0x00000040000000200000001000000008

但是它会打印出 "8 0 16 0",因为在 print_4_num 函数中你读取的是 16 位值而不是 32 位值:

0x0000 0040 0000 0020 0000 0010 0000 0008
     (not used)        [3]  [2]  [1]  [0]

为了方便查看各种函数的作用,请查阅英特尔指令集指南:

https://software.intel.com/sites/landingpage/IntrinsicsGuide/


我只需要重新调整一下 print_4_num 并且对于那个而言已经运作正常了,反正我只需要32位。谢谢。 - pandoragami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接