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

why does _mm_mulhrs_epi16() always do biased rounding to positive infinity?

有谁知道为什么 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)

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

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

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

在质疑在硬件中实现的某些行为之前,我应该有更多的怀疑,因为 int(x + 0.5) 将是一个非常昂贵的错误。

_mm_mulhrs_epi16() 仍然有一些偏差,总是将 x.5 舍入为 + 无穷大。但这对我的应用程序来说不是什么大问题。