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/
原文地址:https://www.cnblogs.com/read-the-spring-and-autumn-annals-in-night/p/12042002.html