相当于lrintf的AVX2

问题描述

我目前有一个简单的C循环,可使用lrintf将数组从float转换为int, 使用认的舍入策略。我想将其放入我的AVX2例程中-是否有与SIMD lrintf等效的命令? 顺便说一句,在lrintf之后,我将结果限制为用户指定的最小值和最大值。

谢谢!

解决方法

lrintf()的原型为long int lrintf (float);时,OP在注释中澄清说,他们正在寻求从float到32位int的转换。

AVX内在_mm256_cvtps_epi32非常适合此操作:它使用当前的舍入模式将float转换为32位int,默认为四舍五入。最接近,甚至是我所熟悉的所有软件环境。

下面的小测试程序的输出应如下所示:

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

但是,我注意到,在我的较旧的Intel编译器上选择高于-O0的任何优化级别都会产生错误的结果,大概是因为编译器会移动__MM_SET_ROUNDING_MODE()实例,而不尊重隐式与周围计算之间的依赖关系。不知道该怎么办。我已经使用最严格的浮点设置进行了编译,并且还尝试添加#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[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[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[7]);

    _MM_SET_ROUNDING_MODE (old_rm);
    return EXIT_SUCCESS;
}