Bit masking & garbage bits

77 Views Asked by At

In the topic "Using the Bitwise Logical Operators" from Herbert Schildt's Java: The Complete Reference, Schildt makes the conjunction ~a & 0x0f in order to "reduce its value [~a] to less than 16, so it can be printed by use of the binary array".

class BitLogic {
public static void main(String[] args) {
    String[] binary = {
    "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
    "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"
    };
    int a = 3; // 0 + 2 + 1 or 0011 in binary
    int b = 6; // 4 + 2 + 0 or 0110 in binary
    /* ... */
    int g = ~a & 0x0f;

    /* ... */
    System.out.println(" ~a = " + binary[g]);
    }
}

Output:

~a = 1100

~a, by itself, doesn't exceed 4 bits, because its value is 1100 (or 12, in decimal), and a conjunction between ~a and 0x0f (0000 1111) should be an identity operation, since every bit of ~a is multiplied by one, hence making ~a depending exclusively on its own values. Am I missing something?

So, how does 0x0f reduce the value of ~a to be less than 16? Wouldn't ~a still have the value 1100 without this conjunction?

1

There are 1 best solutions below

0
Alexandre Rocha On BEST ANSWER

Thanks to harold for their explanation in the comments and pointed QA that, with a few other resources, made me understand it.


One cannot assume that the compiler inverts just the significant bits upon negation. Ex.: 000...01010 has the same value as 1010, but its complement is not the inversion of just those significant digits, it's 111...10101. Therefore, the complement of a in the code provided is actually 111...1100.

Consider N the width of bits that represent an integer type: in Java, all integers are signed values, so their range varies from -2N-1 to 2N-1-1, and the most significant bit (MSB), the one in the Nth position, determines the sign of an integer. When turned on, the MSB represents negative values.

So when a was inverted, its value became 111...1100 which is equivalent to -4 in decimal, because of the way Java and most other programming languages represent their integers: using an encoding known as two's complement, which means that "negative numbers are represented by inverting (changing 1’s to 0’s and vice versa) all of the bits in a value, then adding 1 to the result. To decode a negative number, first invert all of the bits, then add 1." (Herbert Schildt, Java: The Complete Reference 12th).

Finally, by decoding ~a you'll see that it represents -4, in decimal, and that's why, without using a bit mask (What is bit masking?), it throws an ArrayIndexOutOfBoundsException with the message "Index -4 out of bounds for length 16". So the bitwise AND between ~a and 0xf is transforming all but the four rightmost bits of ~a to 0, to then be assigned to g:

    ~a:  1111 .... 1100 
   0xf:  0000 .... 1111 (&)
         ------------------
result:  0000 .... 1100

That's why g requires the 0xf bitmask in order to successfully access the 12th index of the array.