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

标签 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)

最佳答案

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

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

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

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

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

关于rounding - 为什么 _mm_mulhrs_epi16() 总是偏舍入到正无穷大?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28246447/

相关文章:

c++ - 将 float 四舍五入到小数点后一位,然后转换为字符串

swift - 四舍五入 NSDecimalNumber HalfDown

java - 当存储为 float 时,java如何舍入整数

x86 - AVX 或 SSE 上的水平尾随最大值

c++ - float 学矢量化,但整数数学不

c++ - SSE/AVX 对齐内存上的 valarray

visual-studio - 有没有办法强制 visual studio 从 SSE 内在函数生成对齐的指令?

javascript - JS 智能舍入小数

我可以使用舍入来确保原子浮点运算的确定性吗?

c - C 中的 SSE 编程 — 大小不能被 4 整除怎么办?