我试图将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中的成员是什么?
temp2
的成员将是:
{4.014635e-305, 4.062922e-305, 4.111209e-305, 4.159495e-305}
只需将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
来进行对齐。
_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
打印的内容相匹配。 (打印两个额外的0
s,因为指定打印15位数,而只重新排序13位数据,所以其余部分用0
填充。)其他元素也像这样匹配。
操作_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
;认为未对齐版本较慢。
由于此操作,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]