有谁知道为什么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)
一个最严重的错误。我在 上问了同样的问题英特尔开发者论坛 https://software.intel.com/en-us/forums/topic/540117andysem 纠正了我,指出行为是四舍五入到最接近的整数。
我错误地认为这是有偏见的,因为来自MSDN的公式 https://learn.microsoft.com/en-us/previous-versions/bb513995(v=vs.120)
was (x * y + 16384) >> 15
。这看起来非常相似int(x + 0.5)
舍入方法,我知道这种方法对负数有偏见并且畏缩。但我没有意识到负数的右移与除法和转换为 int 不同。
另外,它与我的非 SIMD 参考实现不匹配,结果证明这是有偏差的,因为我正在计算int(sum / 9.0f)
,向零舍入。
在质疑硬件中实现的某些东西的行为之前,我应该有更多的怀疑,因为硬件会经过严格的审查,因为int(x + 0.5)
将是一个非常昂贵的错误。
_mm_mulhrs_epi16()
仍然有一些偏差,总是将 x.5 舍入到+infinity
。但这对我的申请来说不是什么大问题。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)