位运算
1、基础知识
-
计算机中存储的都是补码
-
正数的补码是他本身,负数的补码为反码加1(这里值我们看到的实际的负数,在计算机中其实本身就是存的补码)
-
~
按位取反,^
异或,&
与,|
或- 按位取反计算公式:
~x = -x - 1
- 注意,按位取反和反码的区别,正数也可以按位取反,但是正数的反码是他本身
- 5 = 0000 0000 0000 0000 0000 0000 0000 0101
- 5的反码 = 0000 0000 0000 0000 0000 0000 0000 0101
- ~5 = 1111 1111 1111 1111 1111 1111 1111 1010
- 将其视为某个数的补码,-1为反码:1111 1111 1111 1111 1111 1111 1111 1001
- 取反得到源码: 1000 0000 0000 0000 0000 0000 0000 0110 = -6 (符号位不变)
- 相当于套公式:~5 = -5 - 1
- 按位取反计算公式:
-
<<
左移(乘以2),>>
有符号右移(除2,左边补符号),>>>
无符号右移(除2,左边补0) -
加减乘除
-
加法:先通过异或不带进位的加,在通过与的方式添加进位(JZ65 不用加减乘除做加法)
-
// x + y while(y>0){ // 是否有进位 int tem = x ^ y; // 不进位的加 y = (x & y) << 1; // y保存进位并左移一位 x = tem; // x保存本次结果 }
-
-
减法:将被减数变为负数(被减数变为其相反数,即需要计算机存这个数相反数的补码,按位取反加一)
-
// x - y y = (~y) + 1; add(x,y);
-
-
乘法:先转为正数,通过多次加法实现乘法的效果,再通过是否同号决定符号
-
除法:同上,不停的减去被减数,直到减数小于被减数,记录次数
-
2、模板
(1)位的加法模板
public int add(int x,int y){
// x + y
while(y>0){ // 是否有进位
int tem = x ^ y; // 不进位的加
y = (x & y) << 1; // y保存进位并左移一位
x = tem; // x保存本次结果
}
return x;
}
(2)快速乘模板
-
理解
x * y 举例
2 * 11 = 2 * ( 1011 ) // 转为二进制
2 * 11 = 2 * 1 * 2^0 + 2 * 1 * 2^1 + 2 * 1 * 2^3 = 2 + 4 + 16 = 22
-
所以转变过程为:每次x都乘以2,当y的对应位置为1时,结果ans+=x
// 快速乘
public long quickAdd(long x,long k) {
long ans = 0;
while(k>0){
if((k & 1) == 1){ // 判断这个位置上是不是1
ans += x; // 为1就加x
}
k>>=1; // k就带符号右移,相当于除以2
x += x; // x *= 2
}
return ans;
}