Java 中二进制文字的隐式左填充

Implicit left-padding of the binary literal in Java

当我构造掩码以获取 2 的补码格式中的最高有效位时,我发现了意外行为。

要检查带符号的 8 位数字中的最高有效位是否有效,我可以按如下方式获取该位。

byte value = -1;
long byteSignMask = 0b1000_0000;
value & byteSignMask;

无论我对 byteSignMask 使用 0b1000_0000 还是 1L << 7,结果都是相同的。实际上下面的代码通过了。

long byteSign1 = 1L << 7;
long byteSign2 = 0b1000_0000;
// OK
assertEquals(byteSign1, byteSign2);

但是我做的是int类型;同样,结果也在意料之中。

long intSign1 = 1L << 31;
long intSign2 = 0b1000_0000_0000_0000_0000_0000_0000_0000;

// Fail: expected:<2147483648> but was:<-2147483648>
assertEquals(intSign1, intSign2);

其实是不一样的

// intSign1 = 10000000000000000000000000000000
System.out.println("intSign1 = " + Long.toBinaryString(intSign1));
// intSign2 = 1111111111111111111111111111111110000000000000000000000000000000
System.out.println("intSign2 = " + Long.toBinaryString(intSign2));

看起来整数(intSign1)的文字掩码被左填充了1,而移位操作不会造成这样的效果。

为什么二进制字面量表示的整数自动左补1?是否有描述此行为的任何官方文档?

intSign2 你在这里:

0b1000_0000_0000_0000_0000_0000_0000_0000

是一个int文字,不是一个long文字。

所以你是说“我想要这个位模式表示的 int 值”。

单个 1 后跟 31 个 0 表示为 32 位二进制补码有符号整数 int,是 -2147483648。当您分配给 long 类型变量 intSign2 时,此值会“扩大”为 long。这就是填充 1 的来源。

要使其成为 long 文字,您必须添加 L 后缀:

0b1000_0000_0000_0000_0000_0000_0000_0000L

Why is byteSign2 padded with left 0s, while intSign2 is padded with left 1s?

当你指定一个二进制整数字面量,并且你指定的位数小于数据类型的位大小时,它总是得到 left-padded 和 0s。所以在byteSign2的情况下,你说的是0b1000_0000,其实相当于这个二进制字面量:

0b0000_0000_0000_0000_0000_0000_1000_0000

intSign2 的情况下,您指定了 int 的完整 32 位,所以根本没有填充。

left-padded 1 是 intlong 转换的结果。根据 language specification,这个转换是这样的:

A widening conversion of a signed integer value to an integral type T simply sign-extends the two's-complement representation of the integer value to fill the wider format.

因为转换“sign-extends”,符号位为1补1,符号位为0补0(这样保留数字的符号,负数保持负数等).对于你的二进制文字,符号位是 1,所以它填充 1s。