AVX2 相当于 lrintf
AVX2 equivalent of lrintf
我目前有一个简单的 C 循环,它使用 lrintf
将数组从 float 转换为 int,
使用默认的舍入策略。相反,我想将它放入我的 AVX2 例程中 - 是否有使用 SIMD 的 lrintf
的等效命令?
顺便说一句,在 lrintf
之后,我将结果限制为用户指定的最小值和最大值。
谢谢!
虽然 lrintf()
的原型是 long int lrintf (float);
,但 OP 在评论中澄清说他们正在寻找从 float
到 32 位 int
的转换。
AVX 内在函数 _mm256_cvtps_epi32 非常适合此操作:它使用当前舍入模式提供从 float
到 32 位 int
的转换,默认为 round-to-nearest-even 适用于我熟悉的所有软件环境。
下面这个小测试程序的输出应该如下所示:
source vector: 1.000000 1.100000 1.500000 1.900000 -1.000000 -1.100000 -1.500000 -1.900000
round to nearest: 1 1 2 2 -1 -1 -2 -2
round down: 1 1 1 1 -1 -2 -2 -2
round up: 1 2 2 2 -1 -1 -1 -1
round toward zero: 1 1 1 1 -1 -1 -1 -1
但是,我注意到,选择任何高于 -O0
的优化级别都会给我的旧版英特尔编译器带来不正确的结果,大概是因为编译器移动了 __MM_SET_ROUNDING_MODE()
个实例,而不是尊重 [=31] =]隐式 那些和周围计算之间的依赖关系。不知道该怎么做。我已经使用最严格的 floating-point 设置进行了编译,还尝试添加 #include <fenv.h>
,然后添加 #pragma STDC FENV_ACCESS ON
,但无济于事。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include "immintrin.h"
int main (void)
{
__m256 float_vec;
__m256i int_vec_rn;
__m256i int_vec_rd;
__m256i int_vec_ru;
__m256i int_vec_rz;
float arg[8] = {1.0f, 1.1f, 1.5f, 1.9f, -1.0f, -1.1f, -1.5f, -1.9f};
int32_t res[8];
unsigned int old_rm = _MM_GET_ROUNDING_MODE();
printf ("source vector: % f % f % f % f % f % f % f % f\n",
arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7]);
memcpy (&float_vec, arg, sizeof float_vec);
_MM_SET_ROUNDING_MODE (_MM_ROUND_NEAREST);
int_vec_rn = _mm256_cvtps_epi32 (float_vec);
memcpy (res, &int_vec_rn, sizeof res);
printf ("round to nearest: % d % d % d % d % d % d % d % d\n",
res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7]);
_MM_SET_ROUNDING_MODE (_MM_ROUND_DOWN);
int_vec_rd = _mm256_cvtps_epi32 (float_vec);
memcpy (res, &int_vec_rd, sizeof res);
printf ("round down: % d % d % d % d % d % d % d % d\n",
res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7]);
_MM_SET_ROUNDING_MODE (_MM_ROUND_UP);
int_vec_ru = _mm256_cvtps_epi32 (float_vec);
memcpy (res, &int_vec_ru, sizeof res);
printf ("round up: % d % d % d % d % d % d % d %d\n",
res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7]);
_MM_SET_ROUNDING_MODE (_MM_ROUND_TOWARD_ZERO);
int_vec_rz = _mm256_cvtps_epi32 (float_vec);
memcpy (res, &int_vec_rz, sizeof res);
printf ("round toward zero: % d % d % d % d % d % d % d % d\n",
res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7]);
_MM_SET_ROUNDING_MODE (old_rm);
return EXIT_SUCCESS;
}
我目前有一个简单的 C 循环,它使用 lrintf
将数组从 float 转换为 int,
使用默认的舍入策略。相反,我想将它放入我的 AVX2 例程中 - 是否有使用 SIMD 的 lrintf
的等效命令?
顺便说一句,在 lrintf
之后,我将结果限制为用户指定的最小值和最大值。
谢谢!
虽然 lrintf()
的原型是 long int lrintf (float);
,但 OP 在评论中澄清说他们正在寻找从 float
到 32 位 int
的转换。
AVX 内在函数 _mm256_cvtps_epi32 非常适合此操作:它使用当前舍入模式提供从 float
到 32 位 int
的转换,默认为 round-to-nearest-even 适用于我熟悉的所有软件环境。
下面这个小测试程序的输出应该如下所示:
source vector: 1.000000 1.100000 1.500000 1.900000 -1.000000 -1.100000 -1.500000 -1.900000
round to nearest: 1 1 2 2 -1 -1 -2 -2
round down: 1 1 1 1 -1 -2 -2 -2
round up: 1 2 2 2 -1 -1 -1 -1
round toward zero: 1 1 1 1 -1 -1 -1 -1
但是,我注意到,选择任何高于 -O0
的优化级别都会给我的旧版英特尔编译器带来不正确的结果,大概是因为编译器移动了 __MM_SET_ROUNDING_MODE()
个实例,而不是尊重 [=31] =]隐式 那些和周围计算之间的依赖关系。不知道该怎么做。我已经使用最严格的 floating-point 设置进行了编译,还尝试添加 #include <fenv.h>
,然后添加 #pragma STDC FENV_ACCESS ON
,但无济于事。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include "immintrin.h"
int main (void)
{
__m256 float_vec;
__m256i int_vec_rn;
__m256i int_vec_rd;
__m256i int_vec_ru;
__m256i int_vec_rz;
float arg[8] = {1.0f, 1.1f, 1.5f, 1.9f, -1.0f, -1.1f, -1.5f, -1.9f};
int32_t res[8];
unsigned int old_rm = _MM_GET_ROUNDING_MODE();
printf ("source vector: % f % f % f % f % f % f % f % f\n",
arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7]);
memcpy (&float_vec, arg, sizeof float_vec);
_MM_SET_ROUNDING_MODE (_MM_ROUND_NEAREST);
int_vec_rn = _mm256_cvtps_epi32 (float_vec);
memcpy (res, &int_vec_rn, sizeof res);
printf ("round to nearest: % d % d % d % d % d % d % d % d\n",
res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7]);
_MM_SET_ROUNDING_MODE (_MM_ROUND_DOWN);
int_vec_rd = _mm256_cvtps_epi32 (float_vec);
memcpy (res, &int_vec_rd, sizeof res);
printf ("round down: % d % d % d % d % d % d % d % d\n",
res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7]);
_MM_SET_ROUNDING_MODE (_MM_ROUND_UP);
int_vec_ru = _mm256_cvtps_epi32 (float_vec);
memcpy (res, &int_vec_ru, sizeof res);
printf ("round up: % d % d % d % d % d % d % d %d\n",
res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7]);
_MM_SET_ROUNDING_MODE (_MM_ROUND_TOWARD_ZERO);
int_vec_rz = _mm256_cvtps_epi32 (float_vec);
memcpy (res, &int_vec_rz, sizeof res);
printf ("round toward zero: % d % d % d % d % d % d % d % d\n",
res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7]);
_MM_SET_ROUNDING_MODE (old_rm);
return EXIT_SUCCESS;
}