Java 中负字节和短数据类型的位操作

Bit manipulation in negative byte and short data types in Java

我正在尝试实现 class 来存储 32 位数字而不使用 int 原始类型。为此,我使用两个 short 变量 msbslsbs 来存储数字的 32 位,每个变量 16 位。 变量 msbs 将存储数字的前 16 位,lsbs 变量存储剩余的 16 位。

当涉及到将给定字节保存到变量时,我应用下一个公式:(字节顺序以 Little-Endian 表示法给出)

输入 -> byte[] n = {0b00110101, -3, 0b1001, 0b0}; 到数字 0b00000000 00001001 11111101 00110101 (654645)

msbs = ((n[3] << 8) | n[2]); lsbs = ((n[1] << 8) | n[0]);

如下图

private void saveNumber(byte[] n) {
    msbs = (byte)((n[3] << 8) | n[2]);
    lsbs = (byte)((n[1] << 8) | n[0]);

    System.out.println(Integer.toBinaryString((n[1] << 8) | n[0]));//Prints 11111111111111111111110100110101

    System.out.println("msbs -> " + Integer.toBinaryString(msbs));
    System.out.println("lsbs -> " + Integer.toBinaryString(lsbs));//Prints 110101
}

System.out.println(Integer.toBinaryString((n[1] << 8) | n[0]));//Prints 11111111111111111111110100110101

打印出我所需要的,尽管开头有大量无用的 1 位(我可以通过将其转换为 short 来摆脱) 但是当我打印 lsbs 我存储完全相同的值时(显然)它输出 110101 应该是 0b1111110100110101

为什么会出现这种行为?我知道这一定是 Java 在将值 11111111111111111111110100110101 存储为 16 位原始类型时执行的 "internal" 转换(我个人认为不会发生因为我在一个 8 位数字中向左移动 8 位,这应该给我一个 16 位数字) 作为旁注, msbs 变量正在做我想要它做的事情,所以问题应该与 Java 表示负数的方式有关

顺便说一句,我知道 Java 并不是玩比特的最佳语言。

Why does this behavior occur?

在Java中,所有位运算都是32位或64位运算。这与其他一些语言不同,并且可能出乎意料。但就是这样。

I understand It must be something with the "internal" casting performed by Java ....

Java 在您的任何示例中都没有进行隐式缩小转换1。事实上,我认为意外行为的原因是代码中的显式缩小转换:

  msbs = (byte)((n[3] << 8) | n[2]);

您已将 32 位值从 ((n[3] << 8) | n[2]) 显式转换为 byte。根据您所说的期望,您应该转换为 short.


旁白:当你这样写"Which personally, I think sholdn't be happening ..."时,暗示你在怀疑Java编译器的正确性。事实上,在 99.999% 的情况下 2,真正的问题是有人不理解编译器 应该 对它们做什么;即他们的语言知识太浅3。在大多数情况下,有一个编程语言的规范精确地说明了特定结构的含义。在 Java 的情况下,它是 Java 语言规范。


1 - 事实上,我能想到的唯一情况是在赋值运算符中发生原始类型的内部缩小。

2 - 我编造了这个数字,但关键是编译器错误很少是意外应用程序行为的原因。

3 - 或者这可能只是程序员遗漏的应用程序错误。疲劳会对大脑造成不良影响...