【发布时间】:2016-01-11 22:37:36
【问题描述】:
我正在开发一个应用程序,它将 -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]);
【问题讨论】:
-
你能把它简化为一个独立的示例程序来演示这个问题吗?
-
在执行前后保存参数的值可能很有用。
-
这是 all 浮点处理的默认行为,而不仅仅是 SSE。 Round half to even or banker's rounding 是根据 IEEE 754 标准的默认舍入模式。原因是当应用于多个数字时,这可以最大限度地减少舍入误差,而舍入保证存在半点误差。
标签: c++ x86 sse intrinsics rounding-error