c++ 如何计算一个__m128变量,(还有_mm_blendv_ps做什么)?

cgvd09ve  于 2023-01-18  发布在  其他
关注(0)|答案(1)|浏览(176)

所以我读了英特尔的about _mm_blendv_ps文档,但不太明白这个函数到底是做什么的,所以我写了下面的代码:

__m128 a = { 18.0,4.0,19.0,21.0 };
    __m128 b = { 67.0,92.0,888.0,47.0 };
    __m128 mask = { 1.0,0.0,0.0,1.0 };

    __m128 result = _mm_blendv_ps(a, b, mask);
    cout << "Result is: " << result[0] << " " << result[1] << " " << result[2] << " " << result[4] << endl;

但是我得到错误“没有operator []匹配这些操作数”。为什么我不能访问result?result不是一个32位浮点向量吗?。
那么为什么我不能访问结果呢?我怎么能访问它呢?还有结果cout会是什么(blendv做什么)?

z0qdvdin

z0qdvdin1#

Blendv使用最高设置位在两个结果之间进行选择,它相当于以下代码:

__m128 _mm_blendv_ps(__m128 false_result, __m128 true_result, __m128 mask) {
   __m128 r;
   r[0] = (mask[0] & 0x80000000) ? true_result[0] : false_result[0];
   r[1] = (mask[1] & 0x80000000) ? true_result[1] : false_result[1];
   r[2] = (mask[2] & 0x80000000) ? true_result[2] : false_result[2];
   r[3] = (mask[3] & 0x80000000) ? true_result[3] : false_result[3];
   return r;
}

实际上我倾向于将其打包,因为参数顺序与标准if(cmp) { true } else { false };稍有不同

__m128 select(__m128 mask, __m128 true_result, __m128 false_result) {
   return _mm_blendv_ps(false_result, true_result, mask);
}

通常,您将使用此命令执行if(a < b) {} else {}类型的操作,例如

// if (a < b) {return true_result;} else {return false_result;}
__m128 select_if_lt(__m128 a, __m128 b, __m128 true_result, __m128 false_result) {
   return select(_mm_cmplt_ps(a, b), true_result, false_result);
}

// if (a >= b) {return true_result;} else {return false_result;}
__m128 select_if_ge(__m128 a, __m128 b, __m128 true_result, __m128 false_result) {
   return select(_mm_cmpge_ps(a, b), true_result, false_result);
}

在你上面贴出的代码中:

__m128 mask = { 1.0,0.0,0.0,1.0 };

1.0的最高位实际上是零,所以你需要一个负数来使掩码工作,例如。

// it doesn't matter which negative number you use, 
    // it just requires the sign bit to be set. -0.0f works!
    __m128 mask = { -0.0f,0.0,0.0,-0.0f };

只查看符号位的好处是,您可以执行某些if/else操作,而无需使用比较指令,例如

// if (a < 0) {return true_result;} else {return false_result;}
__m128 select_if_negative(__m128 a, __m128 true_result, __m128 false_result) {
    return select(a, true_result, false_result);
}
  • 但要注意,您将得到-0.0f的假阳性,这对您可能重要,也可能不重要 *。

至于访问__m128的内容,这通常不是跨平台的(有些编译器重载数组运算符,有些指定.x/. y等,有些有内部联合成员变量)。因此,如果你想在跨平台方法中访问内容,你有两个选择:
1.正如Peter正确指出的那样,不要使用_mm_extract_ps,使用_mm_cvtss_f32进行 Shuffle 。

std::ostream& operator << (std::ostream& os, const __m128& v) {
   os << "(" << 
         _mm_cvtss_f32(v) << ", " << 
         _mm_cvtss_f32(_mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))) << ", " << 
         _mm_cvtss_f32(_mm_unpackhi_ps(b, b)) << ", " << 
         _mm_cvtss_f32(_mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))) << ")"; 
    return os;
}

1.使用_mm_store_ps

std::ostream& operator << (std::ostream& os, const __m128& v) {
   float f[4];
   _mm_storeu_ps(f, v);
   os << "(" << 
         f[0] << ", " << 
         f[1] << ", " << 
         f[2] << ", " << 
         f[3] << ")";
    return os;
}

但是,不管您怎么做,访问XMM寄存器的元素总是要付出代价的(当然,除了[0]),所以一般的规则是尽可能避免这样做!

相关问题