Python 四舍五入二次方除法

Python 四舍五入二次方除法,python,bit-manipulation,rounding,bitwise-operators,Python,Bit Manipulation,Rounding,Bitwise Operators,我正在实施教科书中的量化算法。我正处在一个非常有效的阶段,除了四舍五入时我会有一个错误。这就是教科书中所说的: 可通过添加偏移量和按p位位置右移来执行按2^p的舍入除法 现在,我知道右移了,但是他们在说什么偏移量 以下是我的示例代码: def scale(x, power2=16): if x < 0: return -((-x) >> power2) else: return x >> power2 def main

我正在实施教科书中的量化算法。我正处在一个非常有效的阶段,除了四舍五入时我会有一个错误。这就是教科书中所说的:

可通过添加偏移量和按p位位置右移来执行按
2^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()

教科书中提到的偏移量是多少?如何使用它来消除此错误?

偏移量将截断。移位是一个二进制运算符操作。我在这里用方括号表示基数:

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]

按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
def刻度(x,power2=16):
如果x<0:
返回-(((-x)>>(power2-1))+1)>>1)
其他:
返回((x>>(power2-1))+1)>>1

正如所怀疑的那样,您的算法实际上不是四舍五入的,而是截断除法。更重要的是,你的教科书中也有错误。因此,即使修改了算法,也不会得到预期的结果

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

def scale(x, power2=16):
    divider = float(1<<power2)
    result = round(x/divider)
    return result
通过计算四舍五入分割的正确结果,我们可以确认这些预期事实上是错误的:

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 ]

“预期”答案与一个可能的舍入方法(下、最近、上)不一致,这在积极的分红中是显而易见的,在我们考虑负分红引入的并发症之前。

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
因此,向下得到6分,最近的得到5分,向上的只有2分

什么教科书?现在是“点名羞辱”的时候了

进一步试验后更新

如果在进行65536的截断除法之前加上8192,则会得到“预期”结果。没有其他2英寸(512,…,32768)的功率具有相同的效果

将此描述为向向下四舍五入的偏差添加偏移是有点不幸的


重写:对象是四舍五入到最接近的整数,但会引入零偏差(较小的绝对整数)。通过在截断除法之前加上32768,四舍五入到最接近的数值。使用比32768更小的“偏移”可获得所需的偏置效果。如果偏移量是2的幂,例如2**k,可以通过:移位k位,加1,移位16-k位来完成。

我认为正是这种截断应该用作偏移量谢谢你的建议。它解决了我遇到的问题,但引入了新的问题。例如,它不适用于缩放377496:(377496+2**15)>>16=6,而预期答案是5.377496/65536=5.760131836。这四舍五入到6。因此,预期的答案是6,而不是5。或者我误解了你所说的舍入是什么意思了?@misha:根据yuor在其他地方的评论,我认为你在实施DCT之后的量化。此过程不会四舍五入到最接近的整数。将舍入过程偏向较小的整数,可以获得更好的视频质量。在您的代码中,这意味着在移位之前添加一个较小的偏移量。@qbert220:是的,您是对的。我还认为向零舍入可能是更好的方法,因为零系数在熵编码期间会有所帮助。在这种情况下,偏移应该有多小?我可以说可能是二的幂,但不确定是哪一个。谢谢你到目前为止的帮助,顺便说一句,它很棒。四舍五入除法,而不是截断除法division@sverre:是的,更正了。我把“四舍五入”作为一个通用术语(向下四舍五入、向上四舍五入等),谢谢你的意见。我尝试使用您的
缩放功能,但仍然遇到一个错误。例如,
inp:377496预期值:5实际值:6错误:-1
377496/2^16=5.76013…
,所以四舍五入应该是
6
,而不是
5
。对吧?那教科书就有错误了。您的代码提供截断除法。Qbert和我的给出了四舍五入的除法。再次检查教科书上的数字以确定是否正确,但很可能有些数字是错误的。这是教科书:。您可以在上获得有关内容的一些信息,但遗憾的是,示例并不存在。
expect = [ 192, -5, 3, -6, -4, 6, -4, -8, -3, 0, 3, 3, 2, 6, -1, 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