Explanation of the safe average of two numbers

前端 未结 7 1285
离开以前
离开以前 2021-02-08 14:26

Whenever I need to average two numbers for an algorithm like binary search, I always do something like this:

int mid = low + ((high - low) / 2);
<
相关标签:
7条回答
  • 2021-02-08 14:52

    You cannot use an unsigned int in Java. In case of overflow, the low 32 bits are considered, and the high order bits are discarded. The unsigned right shift will help u treat the int as unsigned int. However, in C++ you won't have the overflow.

    0 讨论(0)
  • 2021-02-08 14:55

    The C++ version doesn't solve the overflow issue. It only solves the issue of successfully dividing by 2 using shift instead of /, an optimization that your compiler should be able to make itself if it would be a performance improvement.

    On the other hand overflow may not be a real problem, if your integral types are large enough to hold a reasonable range of indices.

    0 讨论(0)
  • 2021-02-08 14:55

    You are safe from integer overflows by using the way you said you already use, which is:

    int mid = low + ((high - low) / 2);
    

    Let you compiler do it's job to optimize this if it needs to.

    0 讨论(0)
  • 2021-02-08 15:00

    Program synthesis techniques appear to solve such problems.

    In this video, the programmer specifies constraints a) no overflow, b) no division, and c) no if-then-else. The synthesizer automatically came up with something pretty nice.

    https://youtu.be/jZ-mMprVVBU

    0 讨论(0)
  • 2021-02-08 15:03

    The code you saw is broken: it doesn't compute the average of negative numbers correctly. If you are operating on non-negative values only, like indexes, that's OK, but it's not a general replacement. The code you have originally,

    int mid = low + ((high - low) / 2);
    

    isn't safe from overflow either because the difference high - low may overflow the range for signed integers. Again, if you only work with non-negative integers it's fine.

    Using the fact that A+B = 2*(A&B) + A^B we can compute the average of two integers without overflow like this:

    int mid = (high&low) + (high^low)/2;
    

    You can compute the division by 2 using a bit shift, but keep in mind the two are not the same: the division rounds towards 0 while the bit shift always rounds down.

    int mid = (high&low) + ((high^low)>>1);
    
    0 讨论(0)
  • 2021-02-08 15:07

    The C++ version has a hidden cheat: low and high are ints but they're never negative. When you cast them to unsigned int your sign bit becomes an extra precision bit, which a single addition cannot overflow.

    It's not a very good cheat because array indices should be unsigned anyway.

    Like was said elsewhere, i >> 1 means /2 for unsigned integers.

    0 讨论(0)
提交回复
热议问题