SIMD __m256i到__m256d投射结果

问题描述 投票:0回答:3

我试图将SIMD整数变量转换为double。但我看不出这次行动的结果会是什么。例:

int arr[8]={12345678,12333333,12344444,12355555,12366666,12377777,12388888,12399999};
__m256i temp = _mm256_load_si256((__m256i *) arr);
__m256d temp2 = _mm256_castsi256_pd (temp);

作为这个操作的结果,我的temp2中的成员是什么?

c++ x86 simd intrinsics avx2
3个回答
2
投票

Short answer

temp2的成员将是:

{4.014635e-305, 4.062922e-305, 4.111209e-305, 4.159495e-305}

How to obtain the values

只需将SIMD数据写入double阵列并打印即可。

#include <stdio.h>
#include <immintrin.h>

int main(void) {
    int hoge[4]; /* hack that worked on tested environment to avoid Segmentation Fault */
    double result[4];
    int i;

    int arr[8]={12345678,12333333,12344444,12355555,12366666,12377777,12388888,12399999};
    __m256i temp = _mm256_load_si256((__m256i *) arr);
    __m256d temp2 = _mm256_castsi256_pd (temp);

    _mm256_storeu_pd(result, temp2);
    for (i = 0; i < 4; i++) printf("result[%d] = %.6e (%.15a)\n", i, result[i], result[i]);
    return 0;
}

I ran this code on Wandbox得到了这个输出:

result[0] = 4.014635e-305 (0x1.c311500bc614e00p-1012)
result[1] = 4.062922e-305 (0x1.c87e300bc5c7c00p-1012)
result[2] = 4.111209e-305 (0x1.cdeb100bcb34a00p-1012)
result[3] = 4.159495e-305 (0x1.d357f00bd0a1800p-1012)

您可以通过double将SIMD数据写入_mm256_storeu_pd()阵列。

当非32字节对齐的地址传递到_mm256_load_si256()时,可能会生成异常,因此您应该进行对齐。实际上Wandbox上发生了分段错误,所以我插入了虚拟数组hoge来进行对齐。

Why the values were obtained

_mm256_castsi256_pd()实际上只是复制字节并改变它们的解释。

假设使用little-endian并且int是4字节长,arr中的数据在字节寻址内存中是这样的:

data in arr[8]:
|   12345678|   12333333|   12344444|   12355555|   12366666|   12377777|   12388888|   12399999|
byte data in arr[8] (in little endian):
|4e 61 bc 00|15 31 bc 00|7c 5c bc 00|e3 87 bc 00|4a b3 bc 00|b1 de bc 00|18 0a bd 00|7f 35 bd 00|
data seen as 64-bit hex:
|     0x00bc311500bc614e|     0x00bc87e300bc5c7c|     0x00bcdeb100bcb34a|     0x00bd357f00bd0a18|

然后,假设在IEEE754中使用64位double,64位数据由1位符号,11位指数和52位有效数组成。

以第一个元素0x00bc311500bc614e为例,符号位为0(加/ 0),指数为0x00b(11 - 1023 = -1012),有效数为0xc311500bc614e

这与上面的示例代码中通过%.15a打印的内容相匹配。 (打印两个额外的0s,因为指定打印15位数,而只重新排序13位数据,所以其余部分用0填充。)其他元素也像这样匹配。


1
投票

操作_mm256_castsi256_pd确实没什么,它是一个重新解释 - 相当于:

  int v_i;
  double d_i = *((double*)(int*)&v_i).

使用__m256d _mm256_cvtepi32_pd (__m128i a),因为它实际上将4个整数转换为4个双精度。

alignas(16) int arr[4]={12345678,12333333,12344444,12355555};
__m128i temp = _mm_load_si128((__m128i *) arr);
__m256d temp2 = _mm256_cvtepi32_pd(temp);

注意:加载操作_mm_load_si128_mm256_load_si256要求地址正确对齐。否则使用未对齐版本_mm_loadu_si128_mm256_loadu_si256;认为未对齐版本较慢。


1
投票

由于此操作,temp2将包含垃圾。例如,第一条双线将是4.0146351468550722e-305。

这是设计的。 _mm256_castsi256_pd内在函数不转换值,它只重新将寄存器中的位解释为双精度数。

如果你想在寄存器中使用这些双常量,只需使用_mm256_setr_pd内在:

// Set double values to the constants
__m256d temp2 = _mm256_setr_pd( 12345678, 12333333, 12344444, 12355555 );

或者如果这些值不是常数,请使用_mm256_cvtepi32_pd内在函数,这是一个完整的示例:

alignas( 32 ) int arr[ 8 ] = { 12345678, 12333333, 12344444, 12355555,
    12366666, 12377777, 12388888, 12399999 };
__m256i integers = _mm256_load_si256( ( const __m256i* ) &arr );
// Convert first 4 int32 values to doubles
__m256d lowDoubles = _mm256_cvtepi32_pd( _mm256_castsi256_si128( integers ) );
// Convert last 4 values to doubles
__m256d highDoubles = _mm256_cvtepi32_pd( _mm256_extracti128_si256( integers, 1 ) );

这实际上将转换,而不是位转换值。

AVX寄存器保存256位数据。这是__m256i类型中的8个int32值,__m256数据类型中的8个浮点值,但__m256d类型中只有4个double值。

附:您的代码中还存在对齐错误,最好的修复方法是在alignas(32)之前添加int arr[8]

© www.soinside.com 2019 - 2024. All rights reserved.