Reputation: 157
I know that >>
is for signed and >>>
is for unsigned
Similar questions that do not answer my question:
Java, will (low + high) >>> 1 overflow?
Safe integer middle value formula
According to the second link, why do they conclude that:
avg = (low & high) + ((low ^ high) >> 1);
will avoid overflow?
Why can't they just use this instead : (low + high) >> 1
?
(This is for binary search in java)
Upvotes: 2
Views: 175
Reputation: 178263
Yes, (low + high) >> 1
may overflow if the sum is high enough. As you know, >>>
is for unsigned, so that it will always shift in a 0
on the most significant side. This turns out to be very important in case it does overflow.
The trick to using (low + high)
even with overflow is that information is still retained. If it does overflow, the maximum possible mathematical sum is still Integer.MAX_VALUE * 2
, which would still be representable as an unsigned int
, if Java had one. But we can treat the sum as an unsigned int when dividing by 2 by using the unsigned right shift operator, >>>
. When bit-shifting to the right by 1
, this lets us treat the sum as an unsigned int
, "un-overflowing" the int
.
Using >>
here won't work if the sum overflows, because it will overflow into a negative number and >>
will shift in a 1
, keeping the value negative. This results in an incorrect average calculation (2 positive numbers whose sum overflows will result in a negative average).
Whether you're using >>>
or >>
, overflow is a possibility. So both can overflow. But only >>>
will handle the case well, properly "un-overflowing" the sum.
The trick
avg = (low & high) + ((low ^ high) >> 1);
is another way calculating the average when the sum may overflow. This avoids the overflow entirely. This is breaking down the sum into 2 parts -- the "carry" bits and the "non-carry" bits.
When working with addition, the bits that are carried over to the next more significant bit are when both bits are set -- low & high
. Normally in addition, these bits have to be shifted left, but we're calculating the average of 2 numbers, so we would shift right in the end also. The net result: no shift here.
The bits that aren't carried are either a 1
if the bits are different or a 0
if the bits are the same -- that's where (low ^ high)
comes from (XOR). Normally in addition, these bits aren't shifted, but we're calculating the average of 2 numbers, so we would shift right in the end also. This shift shows up as >> 1
. The &
and ^
operators don't overflow, so >>
will work just fine here.
Example: Average of 1100 (12) and 1010 (10)
1100 & 1010 = 1000
1100 ^ 1010 = 0110, 0110 >> 1 = 0011
1000 + 0011 = 1011 (11)
Upvotes: 2