java bit manipulation

17,367

Solution 1

Whoever thought that bytes should be signed when Java was invented should be taken out and beaten with a wet stick of celery until they cry :-)

You can do what you want by casting up to an int and ensuring you never shift a 1 into the top bit, something like this:

byte x = -1;
int x2 = ((int)x) & 0xff;
for(int i = 0; i < 8; i++)
{
    x2 = (x2 >>> 1);
    System.out.println("X: " + x2);
}

Your particular problem is because >>> is casting up to an int to do the shift, then you're casting it back to a byte, as shown here:

byte x = -1;
int x2 = ((int)x) & 0xff;
int x3;
int x4 = x2;
for(int i = 0; i < 8; i++)
{
    x2 = (x2 >>> 1);
    System.out.println("X2: " + x2);
    x3 = (x >>> 1);
    x = (byte)x3;
    x4 = (x4 >>> 1);
    System.out.println("X: " + x3 + " " + x + " " + x4);
}

Which outputs:

X2: 127
X: 2147483647 -1 127
X2: 63
X: 2147483647 -1 63
X2: 31
X: 2147483647 -1 31
X2: 15
X: 2147483647 -1 15
X2: 7
X: 2147483647 -1 7
X2: 3
X: 2147483647 -1 3
X2: 1
X: 2147483647 -1 1
X2: 0
X: 2147483647 -1 0

You can clearly see that x and x3 don't work (even though x3 shifts correctly, casting it back to byte in x sets it to -1 again). x4 works perfectly.

Solution 2

Remember that:

  • operands of bitwise operations are always promoted to at least an int!
  • casts always involve sign extension.

So when you do (x >>> n), even though you defined x as a byte, for the purposes of the shift, it will be first converted to an int. If the byte being converted is negative, then all of the "extra bits" (so, the leftmost 24 bits of the resulting int) added to make it up to an int will be set to 1. Or put another way, if the original byte was -1, the thing you're actually shifting is -1 as an int, i.e. 32-bit number with all 32 bits set to 1. Shifting this right by 1-8 places will still result in the bottom 8 bits all set to 1, hence when you cast back to a byte, you end up with a byte with all 8 bits set to 1, or in other words, a byte value of -1.

Solution 3

I'm not sure about this. But, my guess is that

x >>> 1 

gets promoted to a int from byte, because the literal "1" is an int. Then what you are observing makes sense.

Share:
17,367
jbu
Author by

jbu

Updated on June 05, 2022

Comments

  • jbu
    jbu about 2 years
    byte x = -1;
    for(int i = 0; i < 8; i++)
    {
        x = (byte) (x >>> 1);
        System.out.println("X: " + x);
    }
    

    As I understand it, java stores data in two's-complement, meaning -1 = 11111111 (according to wikipedia).

    Also, from the java docs: "The bit pattern is given by the left-hand operand, and the number of positions to shift by the right-hand operand. The unsigned right shift operator ">>>" shifts a zero into the leftmost position, while the leftmost position after ">>" depends on sign extension. "

    Which means that >>> would shift a 0 to the left most bit every time. So I expect this code to be

    iteration: bit representation of x

    0: 11111111

    1: 01111111

    2: 00111111

    3: 00011111

    ...so on

    However, my output is always X: -1, meaning (I guess) that >>> is putting the sign bit in the left most position. So I then try >>, and same result.

    What's going on? I would expect my output to be: X: -1, x: 127, x: 63, etc.

  • jbu
    jbu over 15 years
    I don't think so.. I changed the code to make sure it wasn't getting promoted to an int: byte x = -1; for(int i = 0; i < 8; i++) { x = (byte) ((byte)x >> (byte)1); System.out.println("X: " + x); } Output was the same, X: -1 every time
  • jbu
    jbu over 15 years
    though this DOES work (I appreciate the help) it doesn't explain to me why my code doesn't work.
  • David Z
    David Z over 15 years
    That doesn't help - sure, you convert x and 1 to bytes, but Java automatically converts them back to ints before applying the right shift operator. There's no way to directly apply a bit-shift to bytes, as far as I know.
  • Ben Clayton
    Ben Clayton over 10 years
    +1 for the celery comment. I've never yet had a situation where I want a byte to be signed....