Java移位运算符:<<,>>,>>>
最近在看jdk的源码,里面还是有很多地方用到了移位操作的,但是本人因为不怎么使用移位操作,基本是看了过一段时间就忘了,忘了用到的时候再看… …
所以在这里写下这篇博客,以后如果再忘记了直接翻回来看。
Java中有三种移位运算符,分别是左移运算符(<<),有符号右移(>>),无符号右移(>>>),作用分别如下:
- 左移运算符(<<):将数值的所有二进制位左移给定的位数,高位即左边的二进制位溢出则丢弃,低位空出来的部分补0
- 有符号右移(>>):将数值的所有二进制位右移给定的位数,高位空出来的补符号位,即负数补1,正数补0,;低位溢出的部分直接丢弃
- 无符号右移(>>>):将数值的所有二进制位右移给定的位数,高位空出来的补0,不论正数还是负数;低位溢出的部分直接丢弃
需要注意的是,移位的所有操作都是对数值的补码进行操作的!!!
我们知道数值有原码、反码、补码三种表示形式,在计算机里的整数值都是以补码的形式存储的。对于正数来说,原码=反码=补码;对于负数来说,反码等于原码除符号位之外各位取反,补码等于反码末位加一。
也就是说我们只需要关心负数的补码即可,并且负数原码反码补码中转换的规则是通用的,即原码=补码除符号位之外各位取反末位加一,而补码=原码除符号位之外各位取反末位加1。当然你也可以先减1然后除符号位之外各位取反。
补码:1000 0000 …这样的数很特殊,它是没有原码的。当数值为16位时,10000000 00000000的真值为-32768,32位时10000000 00000000 00000000 00000000的真值为-2147483648。
我们通过下面的代码来验证:
public class ShiftOptionsDemo {
/**
* 1(int):
* 源码:00000000 00000000 00000000 00000001
* 反码:与源码相同
* 补码:与源码相同
*
* -1(int):
* 源码:10000000 00000000 00000000 00000001
* 反码:11111111 11111111 11111111 11111110
* 补码:11111111 11111111 11111111 11111111
*
* << :左移
* >> : 有符号右移
* >>> : 无符号右移
*
* 左移会将补码的所有比特位向左移动
* 有符号右移将所有比特位移动后“空出来”的比特位会补符号位,负数补1,正数补0
* 无符号右移移动后“空出来”的比特位会补0,不管是正数还是负数
*
* 编译器(可能不是全部的编译器)发现移动的位数等于或超过数值本身的位数后,会
* 对这个移动的位数进行取模操作, 也就是说一个int型的变量x,x << 32 等价于 x << 1
*/
public static void main(String[] args) {
/*
* 结果:4
* 1补码
* 00000000 00000000 00000000 00000001 = 1
* 左移2位后的补码
* 00000000 00000000 00000000 00000100 = 4
*/
System.out.println(1 << 2);
/*
* 结果:-4
* -1补码
* 11111111 11111111 11111111 11111111 = -1
* 左移2位后的补码:
* 11111111 11111111 11111111 11111100
* 转换成原码:
* 10000000 00000000 00000000 00000100 = -4
*/
System.out.println(-1 << 2);
/*
* 结果:0
* 1补码
* 00000000 00000000 00000000 00000001 = 1
* 有符号右移2位后的补码:
* 00000000 00000000 00000000 00000000 = 0
*/
System.out.println(1 >> 2);
/*
* 结果:-1
* -1补码
* 11111111 11111111 11111111 11111111 = -1
* 有符号右移2位后的补码:
* 11111111111111111111111111111111111 = -1
*/
System.out.println(-1 >> 2);
/*
* 结果:0
* 1补码
* 00000000 00000000 00000000 00000001 = 1
* 无符号右移2位后的补码:
* 00000000 00000000 00000000 00000000 = 0
*/
System.out.println(1 >>> 2);
/*
* 结果:1073741823
* -1补码
* 11111111 11111111 11111111 11111111 = -1
* 无符号右移2位后的补码:
* 00111111 11111111 11111111 11111111 = 1073741823
* 这个时候负数就变成了正数
*/
System.out.println(-1 >>> 2);
//输出:1073741823
System.out.println(0b00111111111111111111111111111111);
/*
* 11111111 11111111 11111111 11111111 = -1
* 无符号右移一位后的结果:
* 01111111 11111111 11111111 11111111 = 2147483647
*/
System.out.println(-1 >>> 1);
//输出:2147483647
System.out.println(0b01111111111111111111111111111111);
}
}
大多数情况下,左移1位相当于乘2,有符号右移相当于除2,但是也有很多特殊的数不符合。比如:
010000000 00000000 00000000 0000000本身是个正数,左移1位后就成负数了,并且就是1000 …这样的负数了。
-1有符号右移怎么移都是-1,因为-1的补码是:11111111 … 11111111,根据有符号右移的规则,-1的二进制补码再怎么移都还是它本身。