Java 中负字节和短数据类型的位操作
Bit manipulation in negative byte and short data types in Java
我正在尝试实现 class 来存储 32 位数字而不使用 int
原始类型。为此,我使用两个 short
变量 msbs
和 lsbs
来存储数字的 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 - 或者这可能只是程序员遗漏的应用程序错误。疲劳会对大脑造成不良影响...
我正在尝试实现 class 来存储 32 位数字而不使用 int
原始类型。为此,我使用两个 short
变量 msbs
和 lsbs
来存储数字的 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 - 或者这可能只是程序员遗漏的应用程序错误。疲劳会对大脑造成不良影响...