Java中定义了3种移位运算符,分别是左移运算符“<<”、右移运算符“>>”和无符号右移运算符“>>>”,对于移位运算,移位运算两边的操作数要求为整型,即byte、short、char、int或long类型,或者通过拆箱转换后为整型。当操作数的类型为byte、short或char类型时,会自动提升为int类型,运算的结果也为int类型。对于移位运算,人们对其“误会”实在太深了……
超过自身位数的移位
我们知道,int类型占用4字节,32位,而long类型占用8字节,64位。那么,如果将int类型(long类型)移动超过31位(63位)便失去了意义,因为用通俗的话来说,就是“全移走了”。不过幸运的是,系统对这种情况做了处理。
是怎么处理的呢?普遍都是这样认为的:如果左侧操作数是int类型,会对右侧操作数进行除数为32的求余运算,如果左侧操作数为long类型,会对右侧操作数进行除数为64的求余运算。是的,当要移位的个数为正数时是这样的,但当要移位的个数为负数时却不正确。
例如,假如有如下的赋值运算:
int i = 5 << -10;
-10对32取余还是-10,向左移动-10位,该怎么移动?
实际上,当左侧操作数为int时类型时,右侧操作数只有低5位是有效的(低5位的范围是0~31),也就是说可以看作右侧操作数会先与掩码0x1f(00011111)做与运算,然后左侧操作数再移动相应的位数。类似地,当左侧操作数为long类型时,右侧操作数只有低6位是有效的,可以看作右侧操作数先与掩码0x3f(00111111)做与运算,然后再移动相应的位数。
-10的补码为:
1111 1111 1111 1111 1111 1111 1111 0110
取其低5位,结果为:
1 0110
这个值就是22,也就是相当于:
int i = 5 << 22;
因此,不要把移位运算右侧的操作数与求余运算联系在一起,那是不完全正确的。
移位运算与乘除运算
由于数据采用二进制来表示,因此就会普遍存在这样的想法:左移一位就相当于乘以2,而右移一位就相当于除以2,这种想法正确吗?
在Java中,当两个操作数都是整型的时候,结果也是整型的。如果不能整除,则结果是向0舍入的,也就是说,向靠近0的方向取值。如:
9/2的结果为4
-9/2的结果为-4
而对移位运算来说:
9>>1的结果为4
-9>>1的结果为-5(特殊,移位向下舍入)
永远的-1
“>>>”为无符号右移运算符,其与“>>”不同的是,“>>>”是以0来填补左侧移出的空位,而“>>”是以符号位来填补左侧移出的空位。如果是正数,“>>”与“>>>”是相同的,因为都是用0来补位的,如果是负数,“>>>”就可能移出正数值来。
例:
package deep;
/**
* @ClassName UnsignedRightShift
* @Description 无符号右移
* @author 田爽
* @date 2015年3月24日下午2:01:48
*/
public class UnsignedRightShift {
public static void main(String[] args) {
int k = -1;
System.out.println("int类型移位结果:");
for (int i = 1; i <= Integer.SIZE; ++i) {
k >>>= 1;
System.out.printf("%-15d", k);
if (i % 3 == 0) {
System.out.println();
}
}
System.out.println();
System.out.println("byte类型移位结果:");
byte b = -1;
for (int i = 1; i <= Byte.SIZE; ++i) {
b >>>= 1;
System.out.printf("%-15d", b);
if (i % 3 == 0) {
System.out.println();
}
}
}
}
运行结果:
int类型移位结果:
2147483647 1073741823 536870911
268435455 134217727 67108863
33554431 16777215 8388607
4194303 2097151 1048575
524287 262143 131071
65535 32767 16383
8191 4095 2047
1023 511 255
127 63 31
15 7 3
1 0
byte类型移位结果:
-1 -1 -1
-1 -1 -1
-1 -1
对于int类型变量而言,移位产生了int类型变量的最大值2147483647(2^31),以后每移动一位,值就减半,直到为0。对于byte类型变量,值始终是-1,为什么?
int类型变量-1的补码是:
1111 1111 1111 1111 1111 1111 1111 1111
经过无符号右移后(<<<),使用0补位:
0111 1111 1111 1111 1111 1111 1111 1111
该值即2147483647(2^31)。以此类推,每移动一位,值就会减半,直到所有“1”都被移出,值变为0。
而byte类型变量-1的补码为:
1111 1111
因为是byte类型,所以在参与移位运算之前,会首先扩展为int类型,又因为byte是有符号类型,所以进行符号位扩展,如下:
1111 1111 1111 1111 1111 1111 1111 1111
然而,无符号右移后一位,使用0补位:
0111 1111 1111 1111 1111 1111 1111 1111
因为复合赋值运算符“>>>=”可以自动将结果转换为左侧操作数的类型,因此将结果转换为byte,这只需要进行简单的截断,即丢弃高24位,结果为:
1111 1111
因此,该值还是-1。只要是这样一位一位的移动,不管循环多少次,都是-1,也就是说结果是一个永远的“-1”。