【问题标题】:Implicit left-padding of the binary literal in JavaJava中二进制文字的隐式左填充
【发布时间】:2020-11-25 06:42:22
【问题描述】:

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

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

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

无论我将0b1000_00001L << 7 用于byteSignMask,结果都是相同的。实际上下面的代码通过了。

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?是否有任何官方文档描述这种行为?

【问题讨论】:

    标签: java binary bit-manipulation


    【解决方案1】:

    intSign2你在这里:

    0b1000_0000_0000_0000_0000_0000_0000_0000
    

    int 文字,不是 long 文字。

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

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

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

    0b1000_0000_0000_0000_0000_0000_0000_0000L
    

    为什么byteSign2用左0填充,而intSign2用左1填充?

    当您指定二进制整数文字,并且您指定的位数小于数据类型的位大小时,它将始终以 0 左填充。所以在byteSign2的情况下,你说的是0b1000_0000,其实就相当于这个二进制字面量:

    0b0000_0000_0000_0000_0000_0000_1000_0000
    

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

    左侧填充的 1 是发生 int-to-long 转换的结果。根据language specification,这个转换是这样工作的:

    有符号整数值到整数类型 T 的扩展转换只是符号扩展整数值的二进制补码表示以填充更宽的格式。

    因为转换“符号扩展”,如果符号位为 1,它将填充 1,如果符号位为 0,它将填充 0(这会保留数字的符号,负数保持负数等)。对于您的二进制文字,符号位为 1,因此它填充 1。

    【讨论】:

    • 几乎正确。 1 不是符号位,而是 int-literal 溢出,这就是它被包装到值的负端的原因,如 Integer.MAX_VALUE + 1 == Integer.MIN_VALUE
    • @TreffnonX 解释是否重要?它会在任何地方产生影响吗?另外,我查看了 JLS,第 3.10.1 节,它并没有表明存在溢出。它只是说明0b1000_0000_0000_0000_0000_0000_0000_0000 是最负的二进制整数文字。
    • 是的!如果值不是0b1000_0000_0000_0000_0000_0000_0000_0000,它会有所不同,因为例如0b1000_0000_0000_0000_0000_0000_0000_0001 == Integer.MIN_VALUE + 1 的值。如果它只是一个符号位,那么该值将等于-1。这就是二进制补码的工作原理。这就是它产生巨大差异的原因。
    • @TreffnonX 我认为我们在这里对符号位使用不同的定义:) 在二进制补码中,MSB 也称为符号位,表示-2^32 的位。您可能误认为我使用“符号位”作为符号和大小表示中的符号位?无论如何,我删除了这句话以避免混淆。
    • 好吧,我认为你是对的,MSB 也被称为二进制补码中的符号位,尽管谈论起来令人困惑。它不是十进制系统的符号,而是表示数字范围的符号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 2015-08-28
    • 2011-12-02
    • 1970-01-01
    相关资源
    最近更新 更多