views:

168

answers:

3

Java has 2 bitshift operators for right shifts:

>> shifts right, and is dependant on the sign bit for the sign of the result

>>> shifts right and shifts a zero into leftmost bits

http://java.sun.com/docs/books/tutorial/java/nutsandbolts/op3.html

This seems fairly simple, so can anyone explain to me why this code, when given a value of -128 for bar, produces a value of -2 for foo:

byte foo = (byte)((bar & ((byte)-64)) >>> 6);

What this is meant to do is take an 8bit byte, mask of the leftmost 2 bits, and shift them into the rightmost 2 bits. Ie:

initial = 0b10000000 (-128)
-64 = 0b11000000
initial & -64 = 0b10000000
0b10000000 >>> 6 = 0b00000010

The result actually is -2, which is

0b11111110

Ie. 1s rather than zeros are shifted into left positions

+6  A: 

It's because the & is actually performing promotion to int - which leaves an awful lot of "1" bits. You're then shifting right, leaving the leftmost 2 bits as 0, but then ignoring those leftmost bits by casting back to byte.

This becomes clearer when you separate out the operations:

public class Test
{
    public static void main(String[] args)
    {
        byte bar = -128;
        int tmp = (bar & ((byte)-64)) >>> 6;
        byte foo = (byte)tmp;
        System.out.println(tmp);
        System.out.println(foo);
    }
}

prints

67108862
-2

So to do your bit arithmetic again:

initial = 0b10000000 (-128)
-64 = 0b11000000
initial & -64 = 0b11111111111111111111111110000000 // it's an int now
0b10000000 >>> 6 = 0b00111111111111111111111111100000 // note zero-padding
(byte) (0b10000000 >>> 6) = 11100000 // -2

Even if you get the right result out of the & operation (by casting at that point), >>> will promote the first operand to int first anyway.

EDIT: The solution is to change how you mask things. Instead of masking by -64, mask by just 128+64=192=0xc0 instead:

byte foo = (byte)((bar & 0xc0) >>> 6);

That way you really only get left with the two bits you want, instead of having a load of 1s in the most significant 24 bits.

Jon Skeet
aha! thankyou Jon. Any suggested solutions?
Martin
@Martin: No, because shifting would do the promotion too. Will edit with a solution in a minute.
Jon Skeet
Thankyou very much, clearly your bitfoo is greater than mine ;)
Martin
Note, the >>> should be unnecessary here because the sign bits have already been cleared. Also, the original poster wanted the top two bits which is 0xC0.
PSpeed
+1  A: 

AFAIK, in Java most operators (+,-,>>,& etc.) can't work on anything smaller than ints. So, your bitwise shifts and & are implicitly casting the values into int in the background and then back into byte by your explicit cast outside. The last cast gets rid of the zeroes in the higher bits.

To get results you would expect, try doing this on ints.

MAK
+2  A: 

Others have told you why but I'll break it down further and provide an answer to the real problem, too.

byte foo = (byte)((bar & ((byte)-64)) >>> 6);

Since the & operator will promote everything to int, what this does is essentially:

byte foo = (byte)(((int)bar & (int)((byte)-64)) >>> 6);

If bar is -128 then (int)bar is 0xFFFFFF80 which is then &'ed with 0xFFFFFFC0... which is: 0xFFFFFF80, you then shift that right 6 places to get: 0x3FFFFFFE

The right answer is really simple:

byte foo = (byte)((bar & 0xC0) >> 6);

bar is promoted to an int for the & operation so the only thing left in that int will be the top two bits of the original byte. Then shift it right 6 bits and convert it back to bytes.

PSpeed
you say 0xC0, Jon says 0xD0. I think I shall have to check both your logic here...
Martin
@Martin: 0xC0 is correct. I've fixed my answer. How embarrassing :)
Jon Skeet
I was starting to reach that conclusion, I was onto triple checking it, never doubting the great Skeet! ;)
Martin