AVX2浮点数比较并获得0.0或1.0而不是全0或全1位

时间:2017-04-29 19:05:36

标签: c++ sse simd avx avx2

基本上,在结果向量中,我想为所有输入浮点值保存1.0> 1,对于所有输入浮点值< = 1,为0.0。这是我的代码,

float f[8] = {1.2, 0.5, 1.7, 1.9, 0.34, 22.9, 18.6, 0.7};
float r[8]; // Must be {1, 0, 1, 1, 0, 1, 1, 0}

__m256i tmp1 = _mm256_cvttps_epi32(_mm256_loadu_ps(f));
__m256i tmp2 = _mm256_cmpgt_epi32(tmp1, _mm256_set1_epi32(1));
_mm256_store_ps(r, _mm256_cvtepi32_ps(tmp2));

for(int i = 0; i < 8; i++)
    std::cout << f[i] << " : " << r[i] << std::endl;

但我没有得到正确的结果。这就是我得到的。为什么AVX2关系操作不适合我?

1.2 : 0
0.5 : 0
1.7 : 0
1.9 : 0
0.34 : 0
22.9 : -1
18.6 : -1
0.7 : 0

2 个答案:

答案 0 :(得分:5)

我认为最好使用_mm256_cmp_ps来解决您的问题。为此,我实施了以下计划。这比你想要的更多。如果要保存,则应将所有mask元素设置为1,但如果要保存另一个数字,则可以将掩码值更改为您想要的任何值。

//gcc 6.2, Linux-mint, Skylake 
#include <stdio.h>
#include <x86intrin.h>

float __attribute__(( aligned(32))) f[8] = {1.2, 0.5, 1.7, 1.9, 0.34, 22.9, 18.6, 1.0};
// float __attribute__(( aligned(32))) r[8]; // Must be {1, 0, 1, 1, 0, 1, 1, 0}
// in C++11, use alignas(32).  Or C11 _Alignas(32), instead of GNU C __attribute__.

void printVecps(__m256 vec)
{
    float tempps[8];
    _mm256_store_ps(&tempps[0], vec);
    printf(" [0]=%3.2f, [1]=%3.2f, [2]=%3.2f, [3]=%3.2f, [4]=%3.2f, [5]=%3.2f, [6]=%3.2f, [7]=%3.2f \n",
    tempps[0],tempps[1],tempps[2],tempps[3],tempps[4],tempps[5],tempps[6],tempps[7]) ;

}

int main()
{

    __m256 mask = _mm256_set1_ps(1.0), vec1, vec2, vec3;

    vec1 = _mm256_load_ps(&f[0]);                   printf("vec1 : ");printVecps(vec1); // load vector values from f[0]-f[7]
    vec2 = _mm256_cmp_ps ( mask, vec1, _CMP_LT_OS /*0x1*/);
                                                    printf("vec2 : ");printVecps(vec2); // compare them to mask (less)
    vec3 = _mm256_min_ps (vec2 , mask);             printf("vec3 : ");printVecps(vec3); // select minimum from mask and compared results

    return 0;
}

mask = {1,1,1,1,1,1,1,1}的输出是:

vec1 :  [0]=1.20, [1]=0.50, [2]=1.70, [3]=1.90, [4]=0.34, [5]=22.90, [6]=18.60, [7]=1.00 
vec2 :  [0]=-nan, [1]=0.00, [2]=-nan, [3]=-nan, [4]=0.00, [5]=-nan, [6]=-nan, [7]=0.00 
vec3 :  [0]=1.00, [1]=0.00, [2]=1.00, [3]=1.00, [4]=0.00, [5]=1.00, [6]=1.00, [7]=0.00 

mask = {2,2,2,2,2,2,2,2}是:

vec1 :  [0]=1.20, [1]=0.50, [2]=1.70, [3]=1.90, [4]=0.34, [5]=22.90, [6]=18.60, [7]=1.00 
vec2 :  [0]=0.00, [1]=0.00, [2]=0.00, [3]=0.00, [4]=0.00, [5]=-nan, [6]=-nan, [7]=0.00 
vec3 :  [0]=0.00, [1]=0.00, [2]=0.00, [3]=0.00, [4]=0.00, [5]=2.00, [6]=2.00, [7]=0.00 

这取决于_mm256_min_ps与NaNs的非交换行为,用1.0替换NaN元素。 NaN > 1.0 : NaN : 1.0 = 1.0,因为NaN > anything始终为假。

请注意gcc before 7.0 treats the 128b _mm_min_ps intrinsic as commutative even without -ffast-math(即使它知道minps指令不是)。使用最新的gcc,或确保gcc选择按照此算法所需的顺序使用操作数编译代码。 (或使用clang)。有可能gcc不会将操作数与AVX交换,只能与SSE交换(以避免额外的movapd指令),但最安全的是使用gcc7或更高版本。

答案 1 :(得分:3)

使用时将float转换为int _mm256_cvttps_epi32然后返回的整数是截断的(向零舍入)值。也就是说,值1.2,1.7和1.9被转换为1,因此它们不大于1.

_mm256_cmpgt_epi32的输出不是1,而是&#34;所有的&#34;,来自docs

  

...如果s1数据元素大于s2中的对应元素,则目标向量中的对应元素将设置为全1。

&#34;所有&#34;是使用两个补码整数时,结果显示减去一个。

偏离主题:

  • 为什么使用未对齐的加载和对齐的商店?
  • 您应该查看_mm256_cmp_ps