为什么 Java 负字节的无符号位移如此奇怪?
Why Java unsigned bit shifting for a negative byte is so strange?
我有一个字节变量:
byte varB = (byte) -1; // binary view: 1111 1111
我想查看最左边的两位并执行 6 位的无符号右移:
varB = (byte) (varB >>> 6);
但是我得到 -1 就好像它是 int 类型一样,并且得到 3 只有我换30!
我怎样才能解决这个问题并仅通过 6 位移位获得结果?
原因是与移位时发生的 int
的数字提升相关的符号扩展。值 varB
在移位前被提升为 int
。确实发生了向右的无符号位移位,但是当转换回 byte
时,它的效果会下降,它只保留最后 8 位:
varB (byte) : 11111111
promoted to int : 11111111 11111111 11111111 11111111
shift right 6 : 00000011 11111111 11111111 11111111
cast to byte : 11111111
您可以使用按位与运算符 &
在移位之前屏蔽不需要的位。使用 0xFF
的位与运算仅保留 8 个最低有效位。
varB = (byte) ((varB & 0xFF) >>> 6);
这是现在发生的事情:
varB (byte) : 11111111
promoted to int : 11111111 11111111 11111111 11111111
bit-and mask : 00000000 00000000 00000000 11111111
shift right 6 : 00000000 00000000 00000000 00000011
cast to byte : 00000011
因为这就是 java 中字节的 shifting 在语言中定义的方式:https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.19.
要点是小于 int 的类型被默默地加宽为 int,移位然后变窄。
这使得你的单行有效地等同于:
byte b = -1; // 1111_1111
int temp = b; // 1111_1111_1111_1111_1111_1111_1111_1111
temp >>>= 6; // 0000_0011_1111_1111_1111_1111_1111_1111
b = (byte) temp; // 1111_1111
要仅移动字节,您需要自己使用无符号语义明确地进行扩大转换(并且也需要手动进行缩小转换):
byte b = -1; // 1111_1111
int temp = b & 0xFF; // 0000_0000_0000_0000_0000_0000_1111_1111
temp >>>= 6; // 0000_0000_0000_0000_0000_0000_0000_0011
b = (byte) temp; // 0000_0011
我有一个字节变量:
byte varB = (byte) -1; // binary view: 1111 1111
我想查看最左边的两位并执行 6 位的无符号右移:
varB = (byte) (varB >>> 6);
但是我得到 -1 就好像它是 int 类型一样,并且得到 3 只有我换30!
我怎样才能解决这个问题并仅通过 6 位移位获得结果?
原因是与移位时发生的 int
的数字提升相关的符号扩展。值 varB
在移位前被提升为 int
。确实发生了向右的无符号位移位,但是当转换回 byte
时,它的效果会下降,它只保留最后 8 位:
varB (byte) : 11111111
promoted to int : 11111111 11111111 11111111 11111111
shift right 6 : 00000011 11111111 11111111 11111111
cast to byte : 11111111
您可以使用按位与运算符 &
在移位之前屏蔽不需要的位。使用 0xFF
的位与运算仅保留 8 个最低有效位。
varB = (byte) ((varB & 0xFF) >>> 6);
这是现在发生的事情:
varB (byte) : 11111111
promoted to int : 11111111 11111111 11111111 11111111
bit-and mask : 00000000 00000000 00000000 11111111
shift right 6 : 00000000 00000000 00000000 00000011
cast to byte : 00000011
因为这就是 java 中字节的 shifting 在语言中定义的方式:https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.19.
要点是小于 int 的类型被默默地加宽为 int,移位然后变窄。
这使得你的单行有效地等同于:
byte b = -1; // 1111_1111
int temp = b; // 1111_1111_1111_1111_1111_1111_1111_1111
temp >>>= 6; // 0000_0011_1111_1111_1111_1111_1111_1111
b = (byte) temp; // 1111_1111
要仅移动字节,您需要自己使用无符号语义明确地进行扩大转换(并且也需要手动进行缩小转换):
byte b = -1; // 1111_1111
int temp = b & 0xFF; // 0000_0000_0000_0000_0000_0000_1111_1111
temp >>>= 6; // 0000_0000_0000_0000_0000_0000_0000_0011
b = (byte) temp; // 0000_0011