I am currently trying to wrap my head around bitwise and bit shift operators in Java. Although they make sense to me in simplified toy examples (basically positive integers), my understanding falls apart as soon as negatives are involved, and in some other cases. I tried searching all over the Internet with two search engines and I even checked the Java specification. I can't find any source that properly describes how bitwise and bit shift operators work in Java.
One function in the Java standard library that is especially confusing to me is java.lang.Integer.toUnsignedLong(int)
. The source from OpenJdk is shown here (LGPLv2 with classpath exception), with an excerpt in the Javadoc:
/**
* Converts the argument to a {@code long} by an unsigned
* conversion. In an unsigned conversion to a {@code long}, the
* high-order 32 bits of the {@code long} are zero and the
* low-order 32 bits are equal to the bits of the integer
* argument.
*/
public static long toUnsignedLong(int x) {
return ((long) x) & 0xffffffffL;
}
According to the official documentation reproduced above, "the high-order 32 bits of the long are zero and the low-order 32 bits are equal to the bits of the integer argument." I do not see how this follows from the code inside the method body however.
When reading the method, the following is my train of thought for positive x:
- When the integer is cast to long, its sign bit / most significant bit is zero. As such, the long's sign bit / most significant bit is zero and the low-order bits are equal to those of the integer.
- As the long
0xffffffff
has all ones in the lowest-order 4 bytes, and because only these bytes will have data in them, this mask has no effect and the correct result is returned.
When reading it in the context of a negative x
however, my understanding falls apart:
- When the integer is cst to long, its sign bit / most significant bit is one. As such, the long's sign bit / most significant it is one and the low order bits are equal to those of the integer, except the most significant bit of the fourth least significant byte is zero when it is one in the integer.
- As the long
0xffffffff
has all ones in the lowest-order 4 bytes and zero in the highest-order four bytes, it has the only effect of changing the sign bit on the long, and keeps the incorrect integer in the four least significant bits intact. As such, it returns a wrong answer from this method, where the sign bit of the integer is changed as it moves into the long.
When I test this method, however, I get results consistent with the Javadoc. I suspect that I am misunderstanding one or more fundamental points about bitwise operators in Java or its two's complement integer representation, and I hope that this question can clarify those points.