If the JVM does not do it, you can easily do it yourself.
As noted, right shifts on negative numbers do not behave the same as division because the result is rounded in the wrong direction. If you know that the dividend is non-negative, you can safely replace the division by a shift. If it might be negative, you can use the following technique.
If you can express your original code in this form:
int result = x / (1 << shift);
You can replace it with this optimized code:
int result = (x + (x >> 31 >>> (32 - shift))) >> shift;
Or, alternatively:
int result = (x + ((x >> 31) & ((1 << shift) - 1))) >> shift;
These formulae compensate for the incorrect rounding by adding a small number computed from the sign bit of the dividend. This works for any x
with all shift values from 1 to 30.
If the shift is 1 (i.e., you are dividing by 2) then the >> 31
can be removed in the first formula to give this very tidy snippet:
int result = (x + (x >>> 31)) >> 1;
I have found these techniques to be faster even when the shift is non-constant, but obviously they benefit the most if the shift is constant. Note: For long
x
instead of int
, change 31 and 32 respectively to 63 and 64.
Examining the generated machine code shows that (unsurprisingly) the HotSpot Server VM can do this optimization automatically when the shift is constant, but (also unsurprisingly) the HotSpot Client VM is too stupid to.