Search code examples
javabit-manipulationbitwise-operatorsbitwise-xor

Behavior of bitwise xor using binary literals


I'm curious to know what actually happens on a bitwise comparison using binary literals. I just came across the following thing:

byte b1 = (new Byte("1")).byteValue();

// check the bit representation
System.out.println(String.format("%8s", Integer.toBinaryString(b1 & 0xFF)).replace(' ', '0'));
// output: 00000001

System.out.println(b1 ^ 0b00000001);
// output: 0

So everything behaves as expected, the xor comparison equals 0. However when trying the same with a negative number it won't work:

byte b2 = (new Byte("-1")).byteValue();

// check the bit representation
System.out.println(String.format("%8s", Integer.toBinaryString(b2 & 0xFF)).replace(' ', '0'));
// output: 11111111

System.out.println(b2 ^ 0b11111111);
// output: -256

I would have expected that the last xor comparison also equals 0. However this is only the case if I do an explicit cast of the binary literal to byte:

byte b2 = (new Byte("-1")).byteValue();

// check the bit representation
System.out.println(String.format("%8s", Integer.toBinaryString(b2 & 0xFF)).replace(' ', '0'));
// output: 11111111

System.out.println(b2 ^ (byte)0b11111111);
// output: 0

For me it looks like that before the xor comparison both b1 and 0b11111111 have the same bit representation so even if they get casted to int (or something else) the xor should still equal 0. How do you come to the result of -256 which is 11111111 11111111 11111111 00000000 in binary representation? Why do I have to do an explicit cast to byte in order to obtain 0?


Solution

  • Binary literals without a specific cast represent 32-bit integer values, no matter how many digits there are. For example 0b00000001 is a shorthand for 0b00000000 00000000 00000000 00000001.

    Bitwise comparisons in Java use binary numeric promotion (see the Javadocs). In this specific case it means that both operands get converted to int before the comparison is performed.

    0b11111111 is already representing an int (without leading 0s) and just represents 0b00000000 00000000 00000000 11111111, while b2 is a byte representing the value -1. During the conversion to int the value is preserved and thus b2 is cast to a 32-bit integer representing the same number (-1): 0b11111111 11111111 11111111 11111111.

    The xor then evaluates to 0b11111111 11111111 11111111 00000000 which is the 32-bit binary representation of -256.

    In case the xor comparison is performed using (byte)0b11111111 the binary literal will also be treated as a byte and thus equivalently cast to a 32-bit integer representing -1.

    It is important to note that binary comparisons are performed with either double, float, long or int (as specified in the Javadocs). If there are only other types participating in the comparison (as for example byte) they will be converted to int. That is why the following piece of code will give a compilation error:

    byte b1 = (byte)0b00000001;
    byte b2 = (byte)0b00000001;
    byte b3 = b1 & b2;
    
    >>> error: incompatible types: possible lossy conversion from int to byte
    

    ... because the result of a bitwise comparison of two byte is an int.

    Further reading about the why can be done here: