C++ SSE内部函数导致正常浮点操作返回-1。#INV

C++ SSE内部函数导致正常浮点操作返回-1。#INV,c++,sse,intrinsics,C++,Sse,Intrinsics,我写的SSE方法执行音频处理时遇到问题。我在这里实现了一个基于Intel论文的SSE随机函数: 我还有一个方法,使用SSE执行从浮点到S16的转换,转换非常简单,如下所示: unsigned int Float_S16LE(float *data, const unsigned int samples, uint8_t *dest) { int16_t *dst = (int16_t*)dest; const __m128 mul = _mm_set_ps1((float)INT16_

我写的SSE方法执行音频处理时遇到问题。我在这里实现了一个基于Intel论文的SSE随机函数:

我还有一个方法,使用SSE执行从浮点到S16的转换,转换非常简单,如下所示:

unsigned int Float_S16LE(float *data, const unsigned int samples, uint8_t *dest)
{
  int16_t *dst = (int16_t*)dest;
  const __m128 mul = _mm_set_ps1((float)INT16_MAX);
   __m128 rand;
  const uint32_t even = count & ~0x3;
  for(uint32_t i = 0; i < even; i += 4, data += 4, dst += 4)
  {
    /* random round to dither */
    FloatRand4(-0.5f, 0.5f, NULL, &rand);

    __m128 rmul = _mm_add_ps(mul, rand);
    __m128 in = _mm_mul_ps(_mm_load_ps(data),rmul);
    __m64 con = _mm_cvtps_pi16(in);

    memcpy(dst, &con, sizeof(int16_t) * 4);
  }
}
static inline void FloatRand4(const float min, const float max, float result[4], __m128 *sseresult = NULL)
{
  const float delta  = (max - min) / 2.0f;
  const float factor = delta / (float)INT32_MAX;
  ...
}
如果
sseresult!=NULL
返回
\u m128
结果,而
结果
未使用。 这在第一个循环中执行得很好,但在下一个循环中,
delta
变为
-1.#INF
而不是
1.0
。如果我注释掉行
\uuuum64 con=\umm\ucvtps\upi16(in)问题消失了

我认为FPU正在进入未知状态或其他状态。

  • _mm_load_ps不保证进行对齐加载。浮点*数据可以对齐到4个字节,而不是16个字节
  • memcpy可能会扼杀通过SSE实现的优势,您应该为_m64使用一个store命令,但这里再次强调,注意对齐。 如果无法进行未对齐的流或存储uu m64,我会将其保存在_m128i中,并使用_mm_maskmoveu_si128进行屏蔽写入,或者手动存储这8个字节

混合SSE整数运算和(常规)浮点运算。可能会产生奇怪的结果,因为两者都在相同的寄存器上运行。如果您使用:

_mm_empty()

FPU复位至正确状态。Microsoft已经

感谢您提供的提示,我应该声明在发布的示例中省略了对齐代码,传递给此方法的所有数据都是对齐的。我考虑使用uint8_t数组[8]进行联合以手动复制。但问题总是存在,这样的构造(和memcpy)会导致“存储加载”。因此,将_int64(或其中两个)传输到128位寄存器,并分别执行_mm_maskmoveu_si128或_mm_流*应该更有效。流式处理避免了输出对缓存的污染,这可能很有意义,因为一旦写入,您就不需要立即再次使用它。是的,但如果您现在打算继续使用uuuuum64,您可以使用uumuExtract_pi16(SSE2)来提取字0-3。另一种方法是保留两个输出寄存器,使用
\u mm\u cvtps\u epi32
将2 x 4float转换为int32,然后使用
\u mm\u packs\u epi32
将两者打包为一个向量。然后,在128位寄存器中有8个带符号的16位样本用于一次性存储/流式传输。我选择了
\u mm_extract\u epi16
方法,因为缓冲区可能没有足够的值用于2 x 4浮点转换,并且x64处理器不支持内部函数。哦,我可以很容易地用
con=_mm_或_si128(_mm_slli_epi16(con,8),_mm_srli_epi16(con,8))这不只是因为_mm_cvtps_pi16吗?我以为空的只是MMX。所以我要替换它,因为空的代价很高。是的,更正确的解决方案是删除那些FPU指令并坚持SSE直到完成,但这是正确的答案,因为它解释了为什么会发生。cvtps\U pi16是个坏主意。使用_mm_cvtps_epi32、_mm_packs_epi32和_mm_store_si128/_mm_storeu si128的组合将8个浮点数转换为8个整数,您的问题就解决了!