Java_语法基础_移位运算的真实剖析

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”。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值