値をそれ自体と比較することで、NaN の値をテストできます。x == x
x が NaN の場合は false を返します。したがって、4 x float 値の SSE ベクトル vx の場合:
vmask = _mm_cmpeq_ps(vx, vx);
vx の NaN 要素はすべて 0、非 NaN 要素はすべて 1 のマスク ベクトルが得られます。マスクを使用して NaN をゼロにすることができます。マスクを使用して、有効なデータ ポイントの数を 32 ビット整数のベクトルとして扱い、累積することによってカウントすることもできます。
これは実際に動作するテスト済みの例です。n は 4 の倍数であり、a、b は 16 バイト境界ではないことを前提としています。また、SSE4 が必要なことにも注意してください。
float rms(const float *a, const float *b , int n)
{
int count;
float sum;
__m128i vcount = _mm_set1_epi32(0);
__m128 vsum = _mm_set1_ps(0.0f);
assert((n & 3) == 0);
for (int i = 0; i < n; i += 4)
{
__m128 va = _mm_loadu_ps(&a[i]);
__m128 vb = _mm_loadu_ps(&b[i]);
__m128 vmaska = _mm_cmpeq_ps(va, va);
__m128 vmaskb = _mm_cmpeq_ps(vb, vb);
__m128 vmask = _mm_and_ps(vmaska, vmaskb);
__m128 vtmp = _mm_sub_ps(va, vb);
vtmp = _mm_and_ps(vtmp, vmask);
vtmp = _mm_mul_ps(vtmp, vtmp);
vsum = _mm_add_ps(vsum, vtmp);
vcount = _mm_sub_epi32(vcount, (__m128i)vmask);
}
vsum = _mm_hadd_ps(vsum, vsum);
vsum = _mm_hadd_ps(vsum, vsum);
_mm_store_ss(&sum, vsum);
vcount = _mm_hadd_epi32(vcount, vcount);
vcount = _mm_hadd_epi32(vcount, vcount);
count = _mm_extract_epi32(vcount, 0);
return count > 0 ? sum / (float)count : 0.0f;
}