我目前正在努力理解Java中的位运算符和位移操作符。虽然它们在简化的玩具示例(基本上是正整数)中对我来说很有意义,但是一旦涉及到负数或其他情况时,我的理解就会崩溃。我尝试用两个搜索引擎在互联网上搜索,甚至查看了Java规范文档,但找不到任何正确描述Java中如何使用位运算符和位移操作符的来源。
Java标准库中一个特别令我困惑的函数是java.lang.Integer.toUnsignedLong(int)
。OpenJDK的源代码如下所示(LGPLv2带classpath例外),其中包含摘自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;
}
根据上面的官方文档,“长整型的高32位为零,低32位等于整型参数的位。”然而,我没有看到这如何从方法体内的代码中得出。
对于正数x,我的想法如下: 1. 当整数强制转换为长整型时,其符号位/最高有效位为零。因此,长整型的符号位/最高有效位为零,低位与整数相同。 2. 由于长整型的0xffffffff在最低4个字节中都为1,并且只有这些字节中有数据,因此该掩码没有影响,返回了正确的结果。
但是,当考虑负数x时,我就无法理解了: 1. 当整数强制转换为长整型时,其符号位/最高有效位为1。因此,长整型的符号位/最高有效位也为1,低位与整数相同,除了第四个最低有效字节的最高有效位为0,而这个位在整数中为1。 2. 由于长整型的0xffffffff在最低4个字节中都为1,在最高4个字节中都为零,它唯一的作用是改变长整型的符号位,并保留错误的整数在最低4位中。因此,这个方法返回了一个错误的答案,其中整数的符号位在移动到长整型时被改变了。
然而,当我测试这个方法时,得到的结果与Javadoc一致。我怀疑我对Java中的位运算符或其二进制补码整数表示法中的一个或多个基本要点存在误解,并希望这个问题能够澄清这些要点。
System.out.printf("%s & %s = %s%n", Long.toBinaryString((long) x), Long.toBinaryString(0xffffffffL), Long.toBinaryString(((long)x) & 0xffffffffL));
- Elliott Frisch