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 是 int
到 long
转换的结果。根据 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。
当我构造掩码以获取 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, whileintSign2
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 是 int
到 long
转换的结果。根据 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。