圆2除以2的幂

时间:2011-05-26 07:30:06

标签: python bit-manipulation rounding bitwise-operators

我正在从教科书中实现量化算法。我正处于几乎可以工作的地方,除了我在四舍五入时得到一个一个错误。这就是教科书对此所说的:

  

2^p的舍入除法可以通过添加偏移和右移p位位置来执行

现在,我对正确的转变有所了解,但他们谈论的是什么偏移?

这是我的示例代码:

def scale(x, power2=16):
    if x < 0:
        return -((-x) >> power2)
    else:
        return x >> power2
def main():
    inp = [ 12595827, -330706, 196605, -387168, -274244, 377496, -241980, 
            -545272,  -196605, 24198,   196605,  193584, 104858, 424683,
            -40330,     41944 ]
    expect = [ 192, -5, 3, -6, -4, 5, -3, -8, -3, 0, 3, 3, 1, 6, 0, 0 ]
    actual = map(scale, inp)
    for i in range(len(expect)):
        if actual[i] == expect[i]:
            continue
        print 'inp: % 8d expected: % 3d actual: % 3d err: %d' % (inp[i], 
                expect[i], actual[i], expect[i] - actual[i])
if __name__ == '__main__':
    main()

我正在检查负输入,因为移位负整数似乎与实现有关。

我的输出:

inp:   196605 expected:   3 actual:   2 err: 1
inp:  -387168 expected:  -6 actual:  -5 err: -1
inp:  -196605 expected:  -3 actual:  -2 err: -1
inp:   196605 expected:   3 actual:   2 err: 1
inp:   193584 expected:   3 actual:   2 err: 1

教科书中提到的偏移是什么,我该如何使用它来摆脱这个错误?

4 个答案:

答案 0 :(得分:9)

转变将截断。转变是二元运算符运算。我在这里用方括号表示基数:

196605[10] = 101111111111111111[2]
101111111111111111[2] >> 16[10] = 10[2] = 2[10]

要执行正确的舍入,您需要在轮班之前添加一半的除数。

101111111111111111[2] + 1000000000000000[2] >> 16[10] = 110111111111111111[2] >> 16[10] = 11[2] = 3[10]

答案 1 :(得分:3)

通过p移位得到除法2 ^ p向下舍入(截断)。

如果要除以2 ^ p但舍入到最接近的整数,请执行:

shift-right by (p-1)
add 1
shift-right 1

在您的代码中:

def scale(x, power2=16):
    if x < 0:
        return -((((-x) >> (power2-1)) + 1) >> 1)
    else:
        return ((x >> (power2-1)) + 1) >> 1

答案 2 :(得分:1)

怀疑,您的算法实际上并非舍入,而是截断分割。更重要的是,您的教科书中也存在错误。因此,即使您修复了算法,也无法获得预期的结果。

为了确认结果确实存在缺陷,您可以尝试使用正确的基于浮点的舍入除法函数运行代码:

def scale(x, power2=16):
    divider = float(1<<power2)
    result = round(x/divider)
    return result

然而,我们收到以下错误:

inp:   377496 expected:   5 actual:   6 err: -1
inp:  -241980 expected:  -3 actual:  -4 err: 1
inp:   104858 expected:   1 actual:   2 err: -1
inp:   -40330 expected:   0 actual:  -1 err: 1
inp:    41944 expected:   0 actual:   1 err: -1

通过计算舍入除法的正确结果,我们可以确认这些期望实际上是错误的:

377496 / 65536 = 5,7601 -> should round to 6
104858 / 65536 = 1,600 -> should round to 2
-241980 / 65536 = -3,692 -> should round to -4
104858 / 65536 = 1,600 -> should round to 2
-40330 / 65536 = -0,6154 -> should round to -1
41994 / 65536 = 0,641 -> should round to 1

因此,如果舍入除法是您真正想要的,那么您的期望值列表应为:

expect = [ 192, -5, 3, -6, -4, 6, -4, -8, -3, 0, 3, 3, 2, 6, -1, 1 ]

答案 3 :(得分:1)

“预期”答案与其中一种可能的舍入方法(向下,最近,向上)不一致,而且在我们考虑负股息引入的复杂性之前,从正股息中可以看出这一点。

dividend  exp  float div
   24198    0   0.3692322 DN
   41944    0   0.6400146 D
  104858    1   1.6000061 D
  193584    3   2.9538574  NU
  196605    3   2.9999542  NU
  377496    5   5.7601318 D
  424683    6   6.4801483 DN
12595827  192 192.1970673 DN

所以下降得到8分中的6分,最接近得到5分,而得分只得到2分。

什么教科书?这是“Name'n'Shame”时间!

进一步试验后

更新

如果在截断除法之前添加8192,则得到“预期”结果。没有其他2的幂(512,...,32768)具有相同的效果。

将此描述为向偏向四舍五入添加偏移量有点不幸。

重写:对象是舍入到NEAREST整数,但是向零引入偏差(较小的绝对整数)。舍入到最近将通过在截断分区之前添加32768来完成。使用比32768小的“偏移”给出了期望的偏置效果。如果偏移是2的幂,例如2 ** k,则可以通过以下方式完成:移位k位,加1,移位16-k位。