欢迎转载,转载请务必注明出处:
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