使用AVX与NaN进行比较

时间:2015-08-04 20:31:17

标签: c++ c simd avx

我正在尝试使用英特尔的BPSK内在函数为AVX创建一个快速解码器。我有一组复数表示为交错浮点数,但由于BPSK调制,只需要实部(或偶数索引浮点数)。每个浮动x0时映射到x < 0,如果1则映射到x >= 0。这是使用以下例程完成的:

static inline void
normalize_bpsk_constellation_points(int32_t *out, const complex_t *in, size_t num)
{
    static const __m256             _min_mask = _mm256_set1_ps(-1.0);
    static const __m256             _max_mask = _mm256_set1_ps(1.0);
    static const __m256             _mul_mask = _mm256_set1_ps(0.5);

    __m256                          res;
    __m256i                         int_res;

    size_t i;
    gr_complex                      temp;
    float                           real;

    for(i = 0; i < num; i += COMPLEX_PER_AVX_REG){
            res = _mm256_load_ps((float *)&in[i]);

            /* clamp them to avoid segmentation faults due to indexing */
            res = _mm256_max_ps(_min_mask, _mm256_min_ps(_max_mask, res));

            /* Scale accordingly for proper indexing -1->0, 1->1 */
            res = _mm256_add_ps(res, _max_mask);
            res = _mm256_mul_ps(res, _mul_mask);

            /* And then round to the nearest integer */
            res = _mm256_round_ps(res, _MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC);

            int_res = _mm256_cvtps_epi32(res);

            _mm256_store_si256((__m256i *) &out[2*i], int_res);
    }
}

首先,我在[-1, 1]范围内钳制所有收到的浮点数。然后经过一些适当的缩放后,结果四舍五入到最接近的整数。这会将0.5上方的所有浮点数映射到1以及0.5下的所有浮点数映射到0

如果输入浮点数是正常数字,则该过程正常。但是,由于前一阶段的某些情况,某些输入浮点数可能是NaN-NaN。在这种情况下,'NaN'数字通过_mm256_max_ps()_mm256_min_ps()和所有其他AVX函数传播,导致-2147483648的整数映射,这当然导致我的程序因索引无效而崩溃。

是否有任何解决方法可以避免此问题,或者至少使用NaN0设置为AVX

2 个答案:

答案 0 :(得分:4)

你可以用简单的方式开始,比较和掩饰:(未经测试)

res = _mm256_cmp_ps(res, _mm256_setzero_ps(), _CMP_NLT_US);
ires = _mm256_srl_epi32(_mm256_castps_si256(res), 31);

或转移和xor :(也未经过测试)

ires = _mm256_srl_epi32(_mm256_castps_si256(res), 31);
ires = _mm256_xor_epi32(ires, _mm256_set1_epi32(1));

此版本还将关注NaN的符号(并忽略NaN-ness)。

替代没有AVX2(未测试)

res = _mm256_cmp_ps(res, _mm256_setzero_ps(), _CMP_NLT_US);
res = _mm256_and_ps(res, _mm256_set1_ps(1.0f));
ires = _mm256_cvtps_epi32(res);

答案 1 :(得分:3)

Harold为你真正提出的问题找到了一个很好的解决方案,但是我想明确一点,在钳位时消除NaN值非常简单。如果任一参数是NaN,MINPS和MAXPS只返回第二个参数。所以你需要做的就是交换参数顺序,NaN也会被钳制。例如,以下内容会将NaNs钳制为_min_mask:

res = _mm256_max_ps(_mm256_min_ps(_max_mask, res), _min_mask);