问题描述
最近在学习神书《Java编程思想》(第4版),在学习到第三章移位运算符的时候,讲解里面有这么一段话,一开始让我百思不得其解:
Java 也添加了一种“无符号”右移位运算符( >>>), 它使用了“零扩展”:无论正负,都在高位插入0。这一运算符是 C 或 C++没有的。若对 char,byte 或者 short 进行移位处理,那么在移位进行之前,它们会自动转换成一个 int。只有右侧的5个低位才会用到。这样可防止我们在一个 int 数里移动不切实际的位数。若对一个 long 值进行处理,最后得到的结果也是 long。此时只会用到右侧的 6 个低位,防止移动超过 long 值里现成的位数。但在进行“无符号”右移位时,也可能遇到一个问题。若对 byte 或 short 值进行右移位运算,得到的可能不是正确的结果( Java 1.0 和 Java 1.1 特别突出)。它们会自动转换成 int 类型,并进行右移位。但“零扩展”不会发生,所以在那些情况下会得到-1 的结果。
于是我用书上的例程做了一下编程实践:
//: URShift.java
// Test of unsigned right shift
package c03;
public class URShift {
public static void main(String[] args) {
int i = -1;
i >>>= 10;
System.out.println(i);
long l = -1;
l >>>= 10;
System.out.println(l);
short s = -1;
s >>>= 10;
System.out.println(s);
byte b = -1;
b >>>= 10;
System.out.println(b);
}
} ///:~
程序运行结果如下:
4194303
18014398509481983
-1
-1
到这里我还是有点迷惘,还是不理解为啥会出现上面的结果,然后我改了一下代码进行测试:
byte b = -1;
b >>>= 24;
System.out.println(b);
输出-1
,继续修改代码:
byte b = -1;
b >>>= 25;
System.out.println(b);
输出127
,再思考上面书中的内容,恍然大悟。
分析
出现上述输出的原因是,在byte
和short
类型进行移位运算的时候会首先被转换为int
类型,以byte
为例:
byte b = -1;
b:11111111 (-1)
(int)b:11111111 11111111 11111111 11111111
(int)b>>>10:0000000 00111111 11111111 11111111
b:11111111 (-1)
所以问题产生的根本原因还是在转换成int
类型后移位造成的。
byte b = -1;
b:11111111 (-1)
(int)b:11111111 11111111 11111111 11111111
(int)b >>> 25:0000000 00000000 00000000 01111111
b:01111111 (127)
顺便说一句,int
类型转换为byte
类型是会截取其低8
位,丢弃高24
位,得到的数值可能是错误的,但是byte
转换为int
则没有这个问题,因为是低精度向高精度转换,不会损失精度。
疑问
书里的这段话我还是不知道是什么意思(也许是中文翻译的问题),求各路大神解答:
只有右侧的5个低位才会用到。这样可防止我们在一个 int 数里移动不切实际的位数。若对一个 long 值进行处理,最后得到的结果也是 long。此时只会用到右侧的 6 个低位,防止移动超过 long 值里现成的位数。
注意: 移位运算符的右操作数要完成模 32
的运算(除非左操作数是long
类型, 在这种情况下需要对右操作数模64
)。 例如, 1 << 35
的值等同于1 << 3
或 8
。