java位移运算:左移<<、右移>>、无符号右移>>>、与&,或|,取反~,异或^

186 阅读6分钟

说位移运算之前,先补下二进制

二进制

符号位

用二进制表示有符号整数,最高位作为符号位0正1负 以int(32位)为例,则

  • 最大值为 0111 1111 1111 1111 1111 1111 1111 1111
    2312^{31}-1,即Integer.MAX_VALUE,即2147483647
  • 最小值为 1000 0000 0000 0000 0000 0000 0000 0000
    即-2322^{32}, 即Integer.MIN_VALUE,即-2147483648

正负数

正数的二进制平平无奇,负数却并非正数二进制简单的符号位0换1获得,而是通过二进制补码来表示,即负数二进制补码 = 正数二进制取反码 + 1

如-5二进制:  
5的二进制表示:  0000 0000 0000 0000 0000 0000 0000 0101
取反得到反码:   1111 1111 1111 1111 1111 1111 1111 1010   
反码加1得补码:  1111 1111 1111 1111 1111 1111 1111 1011   

左移运算符 <<

将原二进制数值向左移指定位数,右边补0

 5 << 2
5的二进制:  0000 0000 0000 0000 0000 0000 0000 0101   
左移2位即:  0000 0000 0000 0000 0000 0000 0001 0100 ,即20

所以 5 << 2 = 5 * 222^2 = 20
所以x左移n位,即: x << n = x * 2n2^n,此计算方式在结果小于Inetger.Max_VALUE时适用

那么 5左移32位会怎么样呢?为了便于理解,我们用1左移32位来解释这个问题

1的二进制:  0000 0000 0000 0000 0000 0000 0000 0001
左移30位:   0100 0000 0000 0000 0000 0000 0000 0000  -->没问题
左移31位:   1000 0000 0000 0000 0000 0000 0000 0000  -->符号位被覆盖,此时值为-2147483648
左移32位:   0000 0000 0000 0000 0000 0000 0000 0001  -->循环移位,此时值为1
左移33位:   0100 0000 0000 0000 0000 0000 0000 0010  -->循环移位,此时值为2
左移34位:   0100 0000 0000 0000 0000 0000 0000 0100  -->循环移位,此时值为4
...
左移63位:   0100 0000 0000 0000 0000 0000 0000 0100  -->符号位被覆盖,此时值为-2147483648
左移64位:   0000 0000 0000 0000 0000 0000 0000 0001  -->循环移位,此时值为1

右移运算符 >>

将原二进制数值向右移指定位数,正数左边补0,负数左边补1

 5 >> 2
5的二进制:   0000 0000 0000 0000 0000 0000 0000 0101   
右移2位:     0000 0000 0000 0000 0000 0000 0000 0001  -->即1
右移3位:     0000 0000 0000 0000 0000 0000 0000 0000  -->即0
右移4位:     0000 0000 0000 0000 0000 0000 0000 0000  -->即0

如上所示,正数无论右移多少位,都不会得到负数
所以x右移n位,即: x >> n = x / 2n2^n 向下取整

 -5 >> 2
-5的二进制:  1111 1111 1111 1111 1111 1111 1111 1011   
右移2位:     1111 1111 1111 1111 1111 1111 1111 1110  -->即-2
右移3位:     1111 1111 1111 1111 1111 1111 1111 1111  -->即-1
右移4位:     1111 1111 1111 1111 1111 1111 1111 1111  -->即-1

如上所示,负数无论右移的到的最大值为-1 同样x右移n位,即: x >> n = x / 2n2^n 向下取整

无符号右移运算符 >>>

将原二进制数值向右移指定位数,无论正负,左边都补0

 -5 >>> 2
-5的二进制:  1111 1111 1111 1111 1111 1111 1111 1011   
右移2位:     0011 1111 1111 1111 1111 1111 1111 1110  -->即1073741822

有无符号左移运算吗? 无符号左移即【将原二进制数值向左移指定位数右边补0】,其实就是左移运算了

与 &

对应位都为 1 时,结果为 1;否则为 0

int a = 5;           // 0101
int b = 3;           // 0011
int result = a & b;  // 0001 -> 1

或 |

对应位只要有一个为 1,结果为 1;否则为 0

int a = 5;           // 0101
int b = 3;           // 0011
int result = a | b;  // 0111 -> 7

取反 ~

对应位 0 变为 1,1 变为 0

int a = 5;           // 0000 0000 0000 0000 0000 0000 0000 0101
int result = ~a;    // 1111 1111 1111 1111 1111 1111 1111 1010 -> -6

异或 ^

对应位不同则为 1,相同则为 0

int a = 5;           // 0101
int b = 3;           // 0011
int result = a ^ b;  // 0110 -> 6

一个实例

通过位运算简便的作标记

int a = 0x01;                                                       //1
int b = a << 2;                                                     //4
int c = a << 7;                                                     //128
int d = a << 8;                                                     //256
int valueABC = a | b | c;                                           //133
int valueAC = a | c;                                                //129
System.out.println("valueABC是否包含c:" + ((valueABC & c) == c));    //true
System.out.println("valueABC是否包含d:" + ((valueABC & d) == d));    //false
System.out.println("valueABC去掉b:" + (valueABC & ~b));             //129,即a|c