Rounded division by power of 2

时光怂恿深爱的人放手 提交于 2019-12-05 00:17:36

The shift will truncate. The shift is a binary operator operating. I'm using square brackets to denote the base here:

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

To perform correct rounding you need to add half of your divisor before doing the shift.

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

Shifting by p gives division by 2^p rounded down (truncated).

If you want to divide by 2^p but round to closest integer, do:

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

In your code:

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

As suspected, your algorithm is not actually rounded, but truncating division. What's more is that there are errors in your textbook as well. So even if you fix your algorithm, you wouldn't get the expected results.

In order to confirm that the results are in fact flawed, you can try running your code with a correct, float-based rounded division function:

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

Yet we get the following errors:

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

By calculating correct results for rounding division, we can confirm that these expectations, in fact, are faulty:

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

Thus, if rounded division is what you really want, your list of expected values should be:

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

The "expected" answers are just not consistent with one of the possible rounding methods (down, nearest, up) and that's obvious from the positive dividends, before we consider the complications introduced by negative dividends.

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

So down gets 6 out of 8, nearest gets 5, and up gets only 2.

What textbook? It's "Name'n'Shame" time!

Update after further experimentation:

If you add 8192 in just before you do the truncating division by 65536, you get the "expected" results. No other power of 2 in (512, ..., 32768) has the same effect.

Describing this as adding an offset to bias rounding downwards is somewhat unfortunate.

Rewrite: The object is to round to the NEAREST integer, but introduce a bias towards zero (smaller absolute integers). Rounding to the nearest would be done by adding in 32768 before the truncating division. Using a smaller "offset" than 32768 gives the desired bias effect. If the offset is a power of 2 e.g 2**k, it can be done by: shift k bits, add 1, shift 16-k bits.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!