我正在尝试将SIMD整数变量转换为双精度型。但是我看不到此操作的结果。 示例:
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中有哪些成员?
答案 0 :(得分:2)
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)
您可以通过_mm256_storeu_pd()
将SIMD数据写入double
数组。
将未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|
然后,假设在double
中使用了64位IEEE754,则64位数据由1位符号,11位指数和52位有效数字组成。
以第一个元素0x00bc311500bc614e
为例,符号位为0
(正/零),指数为0x00b
(11-1023 = -1012),有效位数为0xc311500bc614e
。
这与上面的示例代码中通过%.15a
打印的内容匹配。
(由于指定了打印15位数字,而仅对13位数字的数据进行了重新排序,因此打印了另外两个0
,因此,其余部分将用0
填充。)
其他元素也这样匹配。
答案 1 :(得分: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值。
P.S。您的代码中还存在对齐错误,最好的解决方法是在alignas(32)
之前添加int arr[8]
答案 2 :(得分: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
;以为未对齐的版本比较慢。