为什么_mm_mulhrs_epi16()总是偏向四舍五入为正无穷大?

时间:2015-01-30 23:43:07

标签: rounding sse

有谁知道为什么pmulhrsw指令或

_mm_mulhrs_epi16(x):= RoundDown((x * y + 16384)/ 32768)

总是向正无穷大方向发展?对我来说,这对于负数是非常偏向的,因为这样的序列如-0.6,0.6,-0.6,0.6,......不会平均加起来为0.

这种行为是故意的还是无意的?如果它是有意的,可能有什么用?是否有一种简单的方法可以减少偏见?

幸运的是,我可以改变我的操作顺序以获得更少偏见的结果(我的函数是带符号的几何平均值):

__m128i ChooseSign(x, sign)
{
  return _mm_sign_epi16(x, sign)
}
signsDifferent = _mm_srai_epi16(_mm_xor_si128(a, b), 15)   // (a ^ b) >> 15
sign = _mm_andnot_si128(signsDifferent, a)    // !signsDifferent & a
//result = ChooseSign(sqrt(a * b), sign) * fraction   // biased
result = ChooseSign(sqrt(a * b) * fraction, sign)

1 个答案:

答案 0 :(得分:1)

一个最严重的错误。我在Intel developer forums问了同样的问题,并且andysem纠正了我,指出行为是四舍五入到最接近的整数。

我错误地认为它有偏见,因为来自MSDN的公式https://msdn.microsoft.com/en-us/library/bb513995.aspx

是(x * y + 16384)>> 15.这看起来与用于舍入的int(x + 0.5)方法非常相似,我知道这种方法偏向负#并且畏缩。但我没有意识到负数的右移与分割和转换为int不同。 另外,它与我的非SIMD参考实现不匹配,因为我计算int(sum / 9.0f),它向零舍入,结果证明是偏向的。

在质疑硬件中实现的东西的行为之前,我应该有更多的疑问,这将被严格审查,因为int(x + 0.5)将是一个非常昂贵的错误。

_mm_mulhrs_epi16()仍有一些偏向总是将x.5向+无穷大四舍五入。但这对我的申请来说并不是什么大问题。