究竟,按位运算符如何在Java中运行?

问题描述 投票:8回答:1

我目前正试图在Java中围绕按位和位移运算符。虽然它们在简化的玩具示例(基本上是正整数)中对我有意义,但是一旦涉及否定,我的理解就会崩溃,在其他一些情况下。我尝试用两个搜索引擎搜索整个互联网,我甚至检查了Java规范。我找不到任何正确描述按位和位移运算符如何在Java中工作的源代码。

Java标准库中的一个特别令我困惑的函数是java.lang.Integer.toUnsignedLong(int)。这里显示了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位为零,低阶32位等于整数参数的位。”我没有看到这是如何从方法体内的代码得出的。

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

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

然而,当在负面的x的背景下阅读时,我的理解分崩离析:

  1. 当整数为cst到long时,其符号位/最高有效位为1。因此,long的符号位/最重要它是1并且低阶位等于整数的那些,除了第四最低有效字节的最高有效位在整数中为1时为零。
  2. 由于长0xffffffff具有最低4字节中的所有1和最高4字节中的零,因此它具有改变long上的符号位的唯一效果,并且保持4个最低有效位中的不正确整数完好无损。因此,它从此方法返回一个错误的答案,其中整数的符号位在移动到long时会发生变化。

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

java bit-manipulation language-lawyer bitwise-operators
1个回答
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。因此,他们必须被删除。

© www.soinside.com 2019 - 2024. All rights reserved.