为什么左移 java 改变符号值
Why left shifting in java changing the sign value
我正在研究 java。
我想知道为什么 java 会产生这个输出。
我在这里分享代码。
public class vvn {
public static void main(String[] args)
{
byte [] arr = new byte[4];
arr[0] = (byte)157;
arr[1] = 1;
arr[2] = 0;
arr[3] = 0;
System.out.format("read 0x%x 0x%x 0x%x 0x%x \n",arr[3],arr[2],arr[1],arr[0]);
int v = (arr[0] | (arr[1] << 8) | (arr[2] << 16) | (arr[3] << 24));
System.out.format("read 0x%x\n",v);
}
}
我得到的输出是
read 0x0 0x0 0x1 0x9d
read 0xffffff9d
我预计输出应该是 0x0000019d
因为最左边的数字是为符号保留的,所以当您向左移动时,符号会在您提交溢出时发生变化。
看。
您的表达式 (arr[1] << 8) | (arr[2] << 16) | (arr[3] << 24) 等于 Ox100。
所以,你需要额外的 Ox9d,十进制是 157。不幸的是,byte diapasone 是 [-128; 127]。所以,不可能从byte中得到157。而你的 a[0] 等于 -99
.
在Java中,类型byte
是一个从-128到127的有符号值。这意味着(byte)157
会导致溢出,实际上等于-99
.当你输出字节的十六进制值时是不明显的,因为-99
(有符号)和157
(无符号)的二进制表示是相等的(并且格式转换%x
解释值作为无符号值)。
但是,一旦在表达式中使用它,byte
值就会提升为 int
。而-99作为一个int的十六进制表示是0xffffff9d
。您可以通过在您的程序中添加以下行来查看:
System.out.format("0x%x \n", (int)arr[0]);
我认为问题在于 java 中的位运算是在 int
上执行的
所以当你使用
arr[0] | ...
arr[0]
转换为 int
。因此,您得到的不是预期的 0x9d
,而是 0xffffff9d
因此,解决问题需要 &
0xff
掩码,例如
int v = ((arr[0] & 0xff) | ((arr[1] & 0xff) << 8) | ...;
您正在从字节(带符号的 8 位)转换为整数(带符号的 32 位)。最高有效位(最左边的一位)保存符号(参见 two's complement)。
你的 157
是二进制的 10011101
。由于您将此值分配给 有符号字节 (java 没有无符号字节),这实际上是一个负数,-99
.
现在,当您从 byte 转换为 int 时,该值将被保留。在负数的情况下,这意味着将所有位设置为左侧以保持符号性。在您的情况下,10011101
变为 11111111 11111111 11111111 10011101
。
无论如何,在 java 中使用无符号字节是一场噩梦。基本上,您需要用 0xff 屏蔽所有内容(以切断 'ones to the left'),如下所示:
int v = ((arr[0] & 0xff) |
((arr[1] & 0xff) << 8) |
((arr[2] & 0xff) << 16) |
((arr[3] & 0xff) << 24));
很漂亮,不是吗?
更新 1: 另外,您可能对 Guava 的 UnsignedBytes...
感兴趣
更新二: Java 8 Byte 有 toUnsignedInt() 和 toUnsignedLong() 方法。你的计算因此变成:
int v = (Byte.toUnsignedInt(arr[0]) |
(Byte.toUnsignedInt(arr[1]) << 8) |
(Byte.toUnsignedInt(arr[2]) << 16) |
(Byte.toUnsignedInt(arr[3]) << 24));
我正在研究 java。 我想知道为什么 java 会产生这个输出。 我在这里分享代码。
public class vvn {
public static void main(String[] args)
{
byte [] arr = new byte[4];
arr[0] = (byte)157;
arr[1] = 1;
arr[2] = 0;
arr[3] = 0;
System.out.format("read 0x%x 0x%x 0x%x 0x%x \n",arr[3],arr[2],arr[1],arr[0]);
int v = (arr[0] | (arr[1] << 8) | (arr[2] << 16) | (arr[3] << 24));
System.out.format("read 0x%x\n",v);
}
}
我得到的输出是
read 0x0 0x0 0x1 0x9d
read 0xffffff9d
我预计输出应该是 0x0000019d
因为最左边的数字是为符号保留的,所以当您向左移动时,符号会在您提交溢出时发生变化。
看。
您的表达式 (arr[1] << 8) | (arr[2] << 16) | (arr[3] << 24) 等于 Ox100。
所以,你需要额外的 Ox9d,十进制是 157。不幸的是,byte diapasone 是 [-128; 127]。所以,不可能从byte中得到157。而你的 a[0] 等于 -99
.
在Java中,类型byte
是一个从-128到127的有符号值。这意味着(byte)157
会导致溢出,实际上等于-99
.当你输出字节的十六进制值时是不明显的,因为-99
(有符号)和157
(无符号)的二进制表示是相等的(并且格式转换%x
解释值作为无符号值)。
但是,一旦在表达式中使用它,byte
值就会提升为 int
。而-99作为一个int的十六进制表示是0xffffff9d
。您可以通过在您的程序中添加以下行来查看:
System.out.format("0x%x \n", (int)arr[0]);
我认为问题在于 java 中的位运算是在 int
上执行的
所以当你使用
arr[0] | ...
arr[0]
转换为 int
。因此,您得到的不是预期的 0x9d
,而是 0xffffff9d
因此,解决问题需要 &
0xff
掩码,例如
int v = ((arr[0] & 0xff) | ((arr[1] & 0xff) << 8) | ...;
您正在从字节(带符号的 8 位)转换为整数(带符号的 32 位)。最高有效位(最左边的一位)保存符号(参见 two's complement)。
你的 157
是二进制的 10011101
。由于您将此值分配给 有符号字节 (java 没有无符号字节),这实际上是一个负数,-99
.
现在,当您从 byte 转换为 int 时,该值将被保留。在负数的情况下,这意味着将所有位设置为左侧以保持符号性。在您的情况下,10011101
变为 11111111 11111111 11111111 10011101
。
无论如何,在 java 中使用无符号字节是一场噩梦。基本上,您需要用 0xff 屏蔽所有内容(以切断 'ones to the left'),如下所示:
int v = ((arr[0] & 0xff) |
((arr[1] & 0xff) << 8) |
((arr[2] & 0xff) << 16) |
((arr[3] & 0xff) << 24));
很漂亮,不是吗?
更新 1: 另外,您可能对 Guava 的 UnsignedBytes...
感兴趣更新二: Java 8 Byte 有 toUnsignedInt() 和 toUnsignedLong() 方法。你的计算因此变成:
int v = (Byte.toUnsignedInt(arr[0]) |
(Byte.toUnsignedInt(arr[1]) << 8) |
(Byte.toUnsignedInt(arr[2]) << 16) |
(Byte.toUnsignedInt(arr[3]) << 24));