Я работаю над приложением, которое преобразует выборки с плавающей запятой в диапазоне от -1,0 до 1,0 в знаковые 16-битные, чтобы обеспечить точность вывода оптимизированных (SSE) подпрограмм. Я написал набор тестов, которые запускают неоптимизированную версию против версии SSE и сравнивает их вывод.
Прежде чем я начну, я убедился, что режим округления SSE установлен на ближайший.
В моем тестовом примере формула:
ratio = 65536 / 2
output = round(input * ratio)
По большей части результаты точны, но на одном конкретном входе я вижу ошибку для ввода -0.8499908447265625
.
-0.8499908447265625 * (65536 / 2) = -27852.5
Обычный код правильно округляет это значение до -27853
, но код SSE округляет его до -27852
.
Вот используемый код SSE:
void Float_S16(const float *in, int16_t *out, const unsigned int samples)
{
static float ratio = 65536.0f / 2.0f;
static __m128 mul = _mm_set_ps1(ratio);
for(unsigned int i = 0; i < samples; i += 4, in += 4, out += 4)
{
__m128 xin;
__m128i con;
xin = _mm_load_ps(in);
xin = _mm_mul_ps(xin, mul);
con = _mm_cvtps_epi32(xin);
out[0] = _mm_extract_epi16(con, 0);
out[1] = _mm_extract_epi16(con, 2);
out[2] = _mm_extract_epi16(con, 4);
out[3] = _mm_extract_epi16(con, 6);
}
}
Автономный пример по запросу:
/* standard math */
float ratio = 65536.0f / 2.0f;
float in [4] = {-1.0, -0.8499908447265625, 0.0, 1.0};
int16_t out[4];
for(int i = 0; i < 4; ++i)
out[i] = round(in[i] * ratio);
/* sse math */
static __m128 mul = _mm_set_ps1(ratio);
__m128 xin;
__m128i con;
xin = _mm_load_ps(in);
xin = _mm_mul_ps(xin, mul);
con = _mm_cvtps_epi32(xin);
int16_t outSSE[4];
outSSE[0] = _mm_extract_epi16(con, 0);
outSSE[1] = _mm_extract_epi16(con, 2);
outSSE[2] = _mm_extract_epi16(con, 4);
outSSE[3] = _mm_extract_epi16(con, 6);
printf("Standard = %d, SSE = %d\n", out[1], outSSE[1]);