一文带你深入理解位运算


前言

相信同学们听到“位运算”这个词都很熟悉,但是在实际应用中却很少涉及,上至十几年的老程序员,下至两三年的菜鸟,可能都没有真正弄懂过,今天就带大家深入地理解学习一遍。

程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。运位算包括位逻辑运算移位运算,位逻辑运算能够方便地设置或屏蔽内存中某个字节的一位或几位,也可以对两个数按位相加等;移位运算可以对内存中某个二进制数左移或右移几位等。

一. 简介

Java提供了七种位运算

 位运算符  名称  含义  举例
 & 按位与

将参与运算的两个二进制数进行&与运算,如果两个二进制位都是1,则与运算的结果为1,其他全都为0。(0与任意数N&运算都是0)

a&b
 | 按位或

将参与运算的两个二进制数进行|或运算,两个二进制位只要其中1个是1 ,那么就是1,如果2个二进制位都是0则表示0。(0与任意数N|运算都是任意数N)

a|b 
按位异或 将参与运算的两个二进制数进行^异或运算,如果2个二进制位都是0或者都是1,那么就是0,如果两个二进制位不同,则为1。  a^b
按位非 一元操作符,按位取反。每个二进制位上都取相反值,1变成0,0变成1。  ~a
<<  左移 将一个数各二进制位全部向左移动若干位,左移运算没有有符号和无符号左移动,在左移时,移除高位的同时在低位补0 a<<2 
>>  右移 又称为有符号右移。将一个数各二进制位全部向右移动若干位,若参与运算的数字为正数,则在高位补0;若为负数,则在高位补1。 a>>2 
>>>  无符号右移  将一个数各二进制位全部向右移动若干位,右移之后左边都是补上0。忽略符号位   a>>>2
 

 

由位运算操作符衍生而来的有:

&= 按位与赋值,|=  按位或赋值,^= 按位非赋值,<<= 赋值左移,>>= 右移赋值,>>>= 无符号右移赋值

(和 += 一个概念)

 

二. 实操

我们以0、1、2、4、8、16、32这几个数来演示下各运算符的逻辑,首先我们计算出这几个数的二进制分别是:

 0  的二进制数为: 0000 0000 0000 0000
 1  的二进制数为: 0000 0000 0000 0001
 2  的二进制数为: 0000 0000 0000 0010
 4  的二进制数为: 0000 0000 0000 0100
 8  的二进制数为: 0000 0000 0000 1000
16 的二进制数为: 0000 0000 0001 0000
32 的二进制数为: 0000 0000 0010 0000

2.1. &按位与运算

将参与运算的两个二进制数进行&与运算,如果两个二进制位都是1,则与运算的结果为1,其他全都为0。(0与任意数N&运算都是0)

判断奇偶:n & 1 == 1则为奇数,n & 1 == 0则为偶数

判断是否是2的整数次幂:n & (n-1) == 0是2 ^ n,n & (n-1) == 1不是2 ^ n

0&2 = 0
0&4 = 0
2&4 = 0
2&8 = 0
4&12= 4

2.2. |按位或运算:

将参与运算的两个二进制数进行|或运算,两个二进制位只要其中1个是1 ,那么就是1,如果2个二进制位都是0则表示0。(0与任意数N|运算都是任意数N)

0|2 = 2
0|4 = 4
2|4 = 6
2|8 = 10

2.3. ^按位异或运算

将参与运算的两个二进制数进行^异或运算,如果2个二进制位都是0或者都是1,那么就是0,如果两个二进制位不同,则为1。

0^2 = 2
0^4 = 4
2^4 = 6
2^8 = 10

2.4. ~按位非运算

一元操作符,按位取反。每个二进制位上都取相反值,1变成0,0变成1。

~0 = -1
~1 = -2
~2 = -3
~4 = -5
~8 = -9

2.5. 左移(<<)

将一个数各二进制位全部向左移动若干位,左移运算没有有符号和无符号左移动,在左移时,移除高位的同时在低位补0

左移1位相当于乘2,x << n等价于x * (2 ^ n)。

2<<1 = 4
2<<2 = 8
2<<3 = 16
2<<4 = 32

2.6. 右移(>>又称为有符号右移)

将一个数各二进制位全部向右移动若干位,若参与运算的数字为正数,则在高位补0;若为负数,则在高位补1。

右移1位相当于除2,x >> n等价于x / 2n。

32>>1 = 16
32>>2 = 8
32>>3 = 4
32>>4 = 2

2.7. 无符号右移(>>>)

将一个数各二进制位全部向右移动若干位,右移之后左边都是补上0。忽略符号位

32>>>1 = 16
32>>>2 = 8
32>>>3 = 4
32>>>4 = 2

二. 原码、反码、补码

一个数,取绝对值大小转换为二进制数,称其为原码。正常情况下,对计算机的俩个数进行加法运算,由于不知道符号,所以对其取补码后进行运算,正数的补码是其本身,负数的补码是对其正数取反加1。
原码:正数的原码是本身,负数的原码是其正数的原码,并将最高位改为1。

以5为例:

5的二进制数(即原码):0000 0000 0000 0101

原码:0000 0101

反码:1111 1010

补码:1111 1011   (负数的补码是对其正数取反加1)

即得到-5的二进制数:1111 1011

三. 实例

把一个整数拆成2的n次方的数相加
public void pow2() {
    int input = 19;
    int m = 1;
    for (int i =1; i< 32; i++) {
        if((m & input) == m) {
            System.out.println(m);
        }

        m = m << 1;
        if(m > input) {
            break;
        }
    }
}