Java中的移位操作——Java编程思想笔记

来源:互联网 发布:中文翻译缅甸语言软件 编辑:程序博客网 时间:2024/06/06 01:55

欢迎转载,转载请务必注明出处:

http://blog.csdn.net/alading2009/article/details/39968281




        Java中的移位操作包括 <<(无符号左移) 、>>(有符号右移) 和 >>>(无符号右移) 三种,一个简单的示例程序如下:

public class ShiftTest {public static void main(String[] args) {int para = -2147483647;System.out.println("初始变量的值为\t"+Integer.toBinaryString(para)+"\t值为\t"+para);//直接添加0是为了使对比更直观System.out.println("无符号左移一位\t000000000000000000000000000000"+Integer.toBinaryString(para<<1)+"\t值为\t"+(para<<1));System.out.println("有符号右移一位\t"+Integer.toBinaryString(para>>1)+"\t值为\t"+(para>>1));System.out.println("无符号右移一位\t0"+Integer.toBinaryString(para>>>1)+"\t值为\t"+(para>>>1));}}

执行结果如下:

初始变量的值为10000000000000000000000000000001值为-2147483647无符号左移一位00000000000000000000000000000010值为2有符号右移一位11000000000000000000000000000000值为-1073741824无符号右移一位01000000000000000000000000000000值为1073741824

观察二进制补码的显示结果,可见:

1、无符号左移一位,是二进制位整体向左移一位,第31位移到32位的位置上,第30位移到第31位的位置上,。。。,第1位移到第2位的位置上,从而第1位被空出,此时在第一位添加一个0,就得到了无符号左移一位的最后结果,即2。


2、右移操作类似于左移,无符号移位都是在空出的位置添加0,。只是当有符号右移时,符号位永远保持不变,即移位后空出位置上添加的0/1与之前符号位保持一致。




       当移位的位数大于变量所定义的位数时,编译器将对移位的位数取模,比如对int型来说,移位33位,则实际移位为33%32=1,移动了一位

public class Test001_01 {public static void main(String[] args) {// TODO Auto-generated method stubSystem.out.println(Integer.toBinaryString(-1));System.out.println(Integer.toBinaryString(-1<<31)+"\t移位31位");System.out.println(Integer.toBinaryString(-1<<32)+"\t移位32位,取模后移位0位");System.out.println(Integer.toBinaryString(-1<<33)+"\t移位33位,取模后移位1位");}}
执行结果如下:

1111111111111111111111111111111110000000000000000000000000000000移位31位11111111111111111111111111111111移位32位,取模后移位0位11111111111111111111111111111110移位33位,取模后移位1位



       有意思的一点是,当我们左移时,相当于对整型变量进行乘数是2的幂次的乘法运算(二进制情况下,某一位右移一位,其值变为原来的2倍),相应地,右移的时候相当于进行除数是2的幂次的除法运算。

public class ShiftAsMultiply {public static void main(String[] args) {int para = 64;System.out.println("初始值为:"+para);System.out.println("左移一位,相当于变量乘以2^1,结果为:"+(para<<1));System.out.println("右移一位,相当于变量除以2^1,结果为:"+(para>>1));}}

结果为:

初始值为:64左移一位,相当于变量乘以2^1,结果为:128右移一位,相当于变量除以2^1,结果为:32


关于移位操作与直接用除号进行运算的性能对比:

1、直接用除号

public class ShiftAsMultiply {public static void main(String[] args) {long startTime = System.nanoTime();for(int i=0;i<100000;i++){int tmp=64;tmp/=4;//tmp>>=2;}long cost = System.nanoTime()-startTime;System.out.println("进行10万次除法运算耗时(ns):"+cost);}}
在我的机器上连续7次的执行结(ns):1786819,1493765,1500191,1479238,1511644,1686528,1483988

平均耗时:1563167.57 ns


2、使用有符号右移

public class ShiftAsMultiply {public static void main(String[] args) {long startTime = System.nanoTime();for(int i=0;i<100000;i++){int tmp=64;//tmp/=4;tmp>>=2;}long cost = System.nanoTime()-startTime;System.out.println("进行10万次除法运算耗时(ns):"+cost);}}

在我的机器上连续7次的执行结果:1134781,1125841,1568915,1140648,1150426,1133943,1133664

平均耗时:1198316.857 ns


关于移位操作与直接用乘号进行运算的性能对比:

1、直接用乘号

public class ShiftAsMultiply {public static void main(String[] args) {long startTime = System.nanoTime();for(int i=0;i<1000000;i++){int tmp=64;tmp*=4;//tmp<<=2;}long cost = System.nanoTime()-startTime;System.out.println("进行100万次乘法运算耗时(ns):"+cost);}}
新机器上连续执行5次的执行结果:992026,1027505,992025,994819,995378

平均耗时:1000350.6 ns


2、使用有符号左移

public class ShiftAsMultiply {public static void main(String[] args) {long startTime = System.nanoTime();for(int i=0;i<1000000;i++){int tmp=64;//tmp*=4;tmp<<=2;}long cost = System.nanoTime()-startTime;System.out.println("进行100万次乘法运算耗时(ns):"+cost);}}

新机器上连续执行5次的执行结果:989791,990350,990628,991188,990349

平均耗时:990461.2


比较乘除法两种方式的运算结果,发现移位操作要优于直接使用乘除号进行的乘除法操作



        另外使用移位操作进行乘除法时同样要注意溢出,比如int 型的变量,在内存中占4个字节,一个字节8bit,则总共32bit,它可表示的范围为:-2^31~2^31-1,如果变量移位后的值超出这个范围,则溢出,得到的结果不出意外就是错误的。比如我第一个示例程序中,得到结果2的那一次操作就是一次溢出,原值为 -(2^31-1),左移一位,相当于乘以2,则值为 -2*(2^31-1)=-2^32+2,已经溢出了32bit整型的表示范围。


        至于浮点数的二进制表示形式,觉得移位也没什么通用的意义,因为浮点数的二进制形式是另一种定义方式,具体参见《计算机组成原理》


        写得有点乱,凑合着看吧,如有错误,欢迎指出。



欢迎转载,转载请务必注明出处:

http://blog.csdn.net/alading2009/article/details/39968281




1 0