Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java bitshift strangeness

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

like image 257
Martin Avatar asked Jan 31 '10 20:01

Martin


People also ask

What is Bitshift?

Bitshifting shifts the binary representation of each pixel to the left or to the right by a pre-defined number of positions. Shifting a binary number by one bit is equivalent to multiplying (when shifting to the left) or dividing (when shifting to the right) the number by 2.

What is Bitshift right?

The bitwise shift operators are the right-shift operator ( >> ), which moves the bits of an integer or enumeration type expression to the right, and the left-shift operator ( << ), which moves the bits to the left.

What does << mean in Java?

Left shift operator shifts the bits of the number towards left a specified number of positions. The symbol for this operator is <<.

What does bit shifting by 0 do?

1 in binary is 0001 , then bitshifting it by 0 won't do anything, which aligns with what you observed. So any number x << 0 is equivalent to x * 2^0 , which is x * 1 , which is just x .


2 Answers

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.

like image 169
Jon Skeet Avatar answered Nov 15 '22 11:11

Jon Skeet


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.

like image 37
MAK Avatar answered Nov 15 '22 11:11

MAK