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


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

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




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

    public class ShiftTest {
    
    	
    	public static void main(String[] args) {
    
    		int para = -2147483647;
    		
    		System.out.println("初始变量的值为	"+Integer.toBinaryString(para)+"	值为	"+para);
    		
    		//直接添加0是为了使对比更直观
    		System.out.println("无符号左移一位	000000000000000000000000000000"+Integer.toBinaryString(para<<1)+"	值为	"+(para<<1));
    		
    		System.out.println("有符号右移一位	"+Integer.toBinaryString(para>>1)+"	值为	"+(para>>1));
    		
    		System.out.println("无符号右移一位	0"+Integer.toBinaryString(para>>>1)+"	值为	"+(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 stub
    
    		System.out.println(Integer.toBinaryString(-1));
    		System.out.println(Integer.toBinaryString(-1<<31)+"	移位31位");
    		System.out.println(Integer.toBinaryString(-1<<32)+"	移位32位,取模后移位0位");
    		System.out.println(Integer.toBinaryString(-1<<33)+"	移位33位,取模后移位1位");
    		
    	}
    
    }
    执行结果如下:

    11111111111111111111111111111111
    10000000000000000000000000000000	移位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




    版权所有,转载请注明出处 http://www.cnblogs.com/read-the-spring-and-autumn-annals-in-night/
  • 相关阅读:
    复制
    rpm 软件包管理
    xfsdump 备份文件系统
    dhcp服务器(一)
    【转】kafka集群搭建
    postgresql主从配置
    centos7 安装配置postgresql
    centos7 安装配置zookeeper
    zookeeper
    piplinedb 安装配置
  • 原文地址:https://www.cnblogs.com/read-the-spring-and-autumn-annals-in-night/p/12042002.html
Copyright © 2020-2023  润新知