Saturday, September 1, 2012

Halving issue in Binary Search

Here is the way to go. In Java: 
mid = (high+low)/2 may give incorrect result. The maximum positive value (in Java) of int is, 2^(31)-1 or 2147483647 (in binary     0111 1111 1111 1111 1111 1111 1111 1111). The problem is that the intermediate value (high+low) could exceed this maximum int value. For example, if high and low, both are equal to 2^(31)-1, then the intermediate value becomes 

 high        : 0111 1111 1111 1111 1111 1111 1111 1111

+low         : 0111 1111 1111 1111 1111 1111 1111 1111
------------------------------------------------------
Intermediate : 1111 1111 1111 1111 1111 1111 1111 1110

And in 2's complement, this intermediate is


2's complement = 1's complement(Intermediate)+1, hence


(1's complement) 0000 0000 0000 0000 0000 0000 0000 0001

(            +1)                                       1
--------------------------------------------------------
                 0000 0000 0000 0000 0000 0000 0000 0010

So, intermediate = -2. This -2 is like

[1111 1111 1111 1111 1111 1111 1111 11]10, where [...] is the sign extension.
Thus mid = -2/2 = -1, which is incorrect.

If we use mid = low+(high-low)/2, that overflow (intermediate exceeding maximum allowed value of
int) never happens, thus gives correct result.
Another incorrect way is mid = (high+low) >> 1; because the overflow has happened already before the shift and ">>" is the signed shift in Java, thus the intermediate 
[1111 1111 1111 1111 1111 1111 1111 11]10 becomes 
[1111 1111 1111 1111 1111 1111 1111 111]1, which is -1. 
The best way is, mid = (high+low) >>> 1. Because ">>>" is the unsigned shift in Java, the intermediate 
[1111 1111 1111 1111 1111 1111 1111 11]10 becomes
[0111 1111 1111 1111 1111 1111 1111 11]11 which is actually the correct mid (= 2^(31)-1).   

No comments:

Post a Comment