确切地讲,按位运算符如何在Java中工作?

时间:2019-04-10 03:51:47

标签: java bit-manipulation language-lawyer bitwise-operators

我目前正在尝试用Java中的按位和移位运算符来解决问题。尽管在简化的玩具示例(基本上是正整数)中它们对我有意义,但一旦涉及负数,在其他情况下,我的理解就会瓦解。我尝试使用两个搜索引擎在Internet上进行搜索,甚至检查了Java规范。我找不到任何能正确描述Java中按位和移位运算符工作方式的资料。

java.lang.Integer.toUnsignedLong(int)是Java标准库中令我特别困惑的一个函数。此处显示了来自OpenJdk的源(带有类路径异常的LGPLv2),以及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;
}

根据上面复制的官方文档,“ long的高阶32位为零,lower的32位等于integer参数的位。”但是,我从方法主体内部的代码中看不出这是怎么回事。

在阅读该方法时,以下是我对正x的思路:

  1. 将整数强制转换为long时,其符号位/最高有效位为零。这样,长号的符号位/最高有效位为零,而低序位则等于整数。
  2. 由于长0xffffffff的最低4个字节全为1,并且因为只有这些字节中才包含数据,所以此掩码无效,并且返回正确的结果。

但是,如果在否定的x语境下阅读该书,我的理解就会崩溃:

  1. 当整数cst为long时,其符号位/最高有效位为1。这样,长整数的符号位/最高有效位是1,而低序位等于整数的那些位,除了第四低有效字节的最高有效位为整数时为零。
  2. 由于长0xffffffff在最低4字节中全为1,在最高4字节中全为零,因此它唯一的改变长整数上的符号位的作用,并保持不正确的整数保留四个最低有效位。这样,它会从该方法返回错误的答案,即整数的符号位随着移入长整数而改变。

但是,当我测试此方法时,我得到的结果与Javadoc一致。我怀疑我误解了有关Java或其二进制补码整数表示形式的位运算符的一个或多个基本要点,我希望这个问题可以澄清这些要点。

1 个答案:

答案 0 :(得分:5)

按位运算符完全按照您的预期工作。它们是严格的位运算符,根本不考虑位的语义。

有时,使用断点运行代码最简单。对于您的特定示例,我将操作步骤转换为原子语句,并用Long.toString打印了结果。

int x = -57;

// step 1:
long xCast = (long) x;
System.out.println(Long.toString(xCast, 2)); // -1110011 - this is not the bitwise representation however.

long mask = 0xffffffffL;
System.out.println(Long.toString(mask, 2)); // 11111111111111111111111111111111

// step 2:
long result = ((long) x) & mask;
System.out.println(Long.toString(result, 2)); // 11111111111111111111111111000111

步骤1是操作看起来像它的主要原因。在Java中,所有(严格数字)值都是带符号的(字符是无符号的)。这就是说,正如您正确指出的那样,所有最高位都是符号位。但是,有趣的部分是,如果数字为负,其余位的作用是什么。 以下线程已经介绍了“二进制补码”的基础知识: What is “2's Complement”? 维基百科页面也是如此:https://en.wikipedia.org/wiki/Two%27s_complement

简而言之,用Java表示整数:

int zero = 0; // == 0b00000000_00000000_00000000_00000000

int maxPositive = Integer.MAX_VALUE; // == 0b01111111_11111111_11111111_11111111

int minus1 = -1; // == 0b11111111_11111111_11111111_11111111

int minNegative = Integer.MIN_VALUE; // == 0b10000000_00000000_00000000_00000000

所以一切顺利的原因是,如果整数为负数,则在将其强制转换时,整个高32位将转换为1,否则数字的表示值将发生变化。有效地:

int x = 0b11111111_11111111_11111111_11000111;

强制转换为:

long xCast = 0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11000111;

由于开发人员希望该方法仅返回初始设置的位,因此您必须屏蔽结果中的高位。这是在第2步中完成的。

因此,您的示例的答案是:Java中非浮点值的表示是二进制补码,因此,当将值从int智能转换为long时,高位用1填充以表示负数。因此必须将它们删除。