Java移位运算符 “

版权声明:本文为博主原创文章。未经博主同意不得转载。 https://blog.csdn.net/zjx409/article/details/37569055

左移运算符(<<)

基本使用方法

将一个运算对象的各二进制位所有左移若干位(左边的二进制位丢弃,右边补0)。

例:a = a << 2 将a的二进制位左移2位,右补0,

左移1位后a = a *2; 

若左移时舍弃的高位不包括1,则每左移一位。相当于该数乘以2。

举例以及困惑

给出以下的程序,大家能够猜一猜结果是什么?
public class MainClass {
	public static void main(String[] args) {
		long i = 1L << 3;
		System.out.println(Long.toBinaryString(i));
		i = 1L << 63;
		System.out.println(Long.toBinaryString(i));
		i = 1L << 64;
		System.out.println(Long.toBinaryString(i));
		
  
	}
}

以下是输出的结果:
1000<pre name="code" class="java">100000000000000000000000000000000000000000000000000000000000000
1


是不是跟想象的不同?上面明明说过左边的二进制位丢弃。如今左移64位不应该是0吗?怎么会出现1呢?难道是循环移位吗?

详解

首先举一个样例来说明不是循环移位:
假设上面的程序改为

i = 3L << 63

程序的结果仍然为

1000000000000000000000000000000000000000000000000000000000000000

那么就说明Java中的移位运算不是循环的。


那对上面的问题又怎么解释呢?

在JLS(Java Language Specific 15.19)中有例如以下解释:
If the promoted type of the left-hand operand is int, only the five lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & (§15.22.1) with the mask value 0x1f (0b11111). The shift distance actually used is therefore always in the range 0 to 31, inclusive.

If the promoted type of the left-hand operand is long, then only the six lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & (§15.22.1) with the mask value 0x3f (0b111111). The shift distance actually used is therefore always in the range 0 to 63, inclusive.

意思是说:在移位运算中,假设被移位的操作数是int类型的,那么仅仅会用到移位数的最低5位,假设是long类型的,那么仅仅会用到低六位。
那么为什么是低5位和低6位呢?相信你应该明确了,int共占32位,long占64位,正好是2的5次幂和6次幂。能够理解为分别对32 和 64 取模。所以1L << 64 就会变成 1L << 0。结果自然就是1了。

关于网上的说法:
网上有很多资料说上述定义是由编译器完毕的,即假设写 1L << 64 。则编译器会将文件编译为  1L << 0 。可是经过本人的实验发现这个过程会发生在执行时而不是编译位class文件的过程。以下是个人所做的一些实验。
实验过程:
  1. 将程序编译为class文件
  2. 使用javap输出class文件的内容
  3. 使用HSDIS输出虚拟机执行的汇编代码

源程序:
public class SF{
	public static void main(String[] args) {
		new SF().sh(1,2);
	}

	public int sh(int a , int b){
		return (a << 32);
	}
}

注意此处使用的是int类型

在windows环境下的批处理文件
javac SF.java
javap -verbose SF > sfp.txt
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=dontinline,*SF.sh -XX:CompileCommand=compileonly,*SF.sh SF > sfasm.txt
pause

这里须要用到HSDIS插件才干输出汇编代码。

能够到https://kenai.com/projects/base-hsdis/downloads下载,可是上面并不提供windows版本号的插件,能够到http://hllvm.group.iteye.com/下载windows X86的插件。


以下是javap的结果。



iload_1 为取得參数a,在栈中push  32  后,进行移位操作。ishl中的i指代的是int的移位操作。

再看反汇编的输出:

[Verified Entry Point]
  0x01c92e50: mov    %eax,-0x4000(%esp)
  0x01c92e57: push   %ebp
  0x01c92e58: sub    $0x18,%esp         ;*iload_1
                                        ; - SF::sh@0 (line 7)

  0x01c92e5b: shl    $0x0,%edx
  0x01c92e5e: mov    %edx,%eax
  0x01c92e60: add    $0x18,%esp
  0x01c92e63: pop    %ebp
  0x01c92e64: test   %eax,0x140100      ;   {poll_return}

看到在分配完栈空间后,在0x01c92e5b这一行中,进行了移位。操作数为0x0



posted on 2019-04-16 11:51  xfgnongmin  阅读(310)  评论(0编辑  收藏  举报

导航