看上去的位运算

153 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情

不管是在学java还是操作系统或者计算机组成原理都不可避免的会学到位运算。最近我在看一些算法题,也碰到了他。想了想,觉得可以整理整理我碰到的位运算。

什么是位运算?

在计算机里面,所有数据都是按照二进制方式存储,即不管你存储是图片还是数字,计算机都会把他转换成01形式存储,在这种状态下,对二进制数据进行(+ - * /)就是我们的位运算,也就是符号参与的二进制运算。

如果有:int a=3;int b=4;int c=a+b; 那么计算机底层是怎么做的呢?

  • 第一步:二进制转换
  • 3: 0 0 0 0 0 0 1 1
  • 4: 0 0 0 0 0 1 0 0
  • ————————————————————
  • 7: 0 0 0 0 0 1 1 1

看到这里,是不是在想怎么把十进制转换二进制?下面就是第一个点,进制转换

进制转换

十进制 以0-9这九个数字组成,不能以0开头。 二进制: 由0和1两个数字组成 八进制: 由0-7数字组成,为了区分与其他进制的数字区别,开头都是以0开始 十六进制:由0-9和A-F组成。为了区分于其他数字的区别,开头都是以0x开始

本文主要讲解十进制转换成二进制。

十进制转二进制的转换原理:除以2,反向取余数,直到商为0终止。 即将某个十进制数除2得到的整数部分保留,作为第二次除2时的被除数,得到的余数依次记下,重复上述步骤,直到整数部分为0就结束,将所有得到的余数最终逆序输出,则为该十进制对应的二进制数。

image.png

如果是小数部分:采用乘2取整的方法(我目前很少碰到)

Java代码实现

  1. Integer b=Integer.toBinaryString(十进制整数)
  2. BigInteger b=Integer.toString(十进制整数, 2);
  3. 依照数学公式,程序中x%2的结果为余数,即二进制中最后一位数字,x=x/2即用新的x求余,最终我们要得出二进制数,就要将余数逆向输出,由于输出后只是表达方式不同(只有0,1),但仍然是十进制数,所以逆向输出就是:第一个得出的余数是个位数,第二个得出余数是十位数。故可以利用累乘10后再累加得出最终的二进制数。
public int Two(int x) {
int t=0; 
int a;
for(int i=1;x>0;i=i*10) {
a=x%2; 
x=x/2; 
t=t+a*i; 
} 
return t;
}

快速十进制转换二进制参考表格:

二进制十进制
00000
00011
00102
00113
01004
01015
01106
01117
10008
10019
101010
101111
110012
111113

运算符号

image.png

& 按位与

运算规则:

0&0=0 0&1=0 1&0=0 1&1=1

巧记:同1则1,有0则0

例如:3&5 即 0000 0011& 0000 0101 = 0000 0001,因此 3&5 的值得1。

与运算用途

  1. 清0 任何数和0做与运算,结果为0

  2. 取指定位数 当前为和1做与运算结果为当前数。比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。

  3. 判断奇偶 只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。

| 或运算符

运算规则:

0|0=0 0|1=1 1|0=1 1|1=1

有1则1,同0为0

例如:3|5即 0000 0011| 0000 0101 = 0000 0111,因此,3|5的值得7。

或运算用途 对数据的某些位数设置为1

任何位上的数和1做或运算,结果为1。 比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。

^ 异或运算符

运算规则:

0^0=0 0^1=1 1^0=1 1^1=0

两个不相同结果是1,相同结果是0

异或几种性质

  • 交换律
  • 结合律 (a^b)^c == a^(b^c)
  • 对于任何数x,都有 x^x=0,x^0=x
  • 自反性: a^b^b=a^0=a; 异或的用途
  1. 翻转指定位数 比如将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X^Y=1010 0001)即可得到。
  2. 和0异或结果不变

左移右移

左移:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。 右移:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。

一些算法使用的

交换 a b 两个数

一般做法: int c=a;a=b;b=c

但是一般那种需要一个新变量,如果没有变量怎么办?优化一下: a=a+b;b=a-b;a=a-b

假设a=10,b=20 a=a+b a=30,b=20 b=a-b a=30,b=10 a=a-b a=20,b=10

但是这样可能会越界再次优化:a=a^b;b=a^b;a=a^b;

假设a=10,b=20 a=0000 1010 b=0001 0100 a=a^b;0000 1010 ^ 0001 0100 =0001 1110 b=a^b;0001 1110 ^ 0001 0100 =0000 1010 a=a^b;0001 1110 ^ 0000 1010 =0001 0100

到这里,交换两个数完成。虽然在平时交换一般使用的是用新变量那种,但是也需要了解了解其他的哦~

左移右移

x<<2就是x右移两位,再赋值,类似x+=2

3<<1 0000 0011 0000 0110 3>>1 0000 0011 _000 0001 _是符号位,原来是0就是0,原来是1就是1 0000 0001

3>>>1 无符号移动(都是0补位,不论符号:可能存在负数移动变成正数那种) 00000011 00000001

& 和 && 的区别

& 逻辑与,按位与

&& 短路与

逻辑符号算:

f&f=f;f&t=f;t&f=f;t&t=t

f&&f=f;f&&t=f;t&&f=f;t&&t=t

看上去两个非常相似,但是&&当第一个是f会发生短路,&&后的不运算返回f。只有第一个是t才会让&&后运算执行