概述
任何一台计算机内部保存的是二进制,再进行逻辑运算和算术运算的时候,计算机只会对二进制执行运算,而计算机对二进制执行的运算叫做位运算。
举例说明,计算机执行3 + 5是如何计算的。
3: 0 0 0 0 0 0 1 1
5: 0 0 0 0 0 1 0 1
8: 0 0 0 0 1 0 0 0
就这样讲十进制转换成二进制后,执行简单的0,1计算,我们平时再写代码的时候常用的运算符为(+,-,*,/,%),但是如何我们能合理的使用位运算,将很大程度上提高我们代码执行的效率。
位运算符
符号 | 名称 | 作用 |
---|---|---|
& | 与 | 都为1,才为1 |
| | 或 | 有一个1,都可以为1 |
^ | 异或 | 相同为0,不同为1 |
~ | 取反 | 1变为0,0变为1 |
<< | 左移 | 所有位向左移动若干位,高位溢出丢弃,低位补0 |
>> | 右移 | 所有位向左移动若干位,分有符号位和无符号位 |
按位与(&)
先看与运算逻辑:
1 & 1 = 1, 1 & 0 = 0, 0 & 1= 0, 0 & 0 = 0
当第一个数为0时,此时形成断路,剩下的表达式不会执行,程序就会输出0
input: 2 & 2
output: 2
input: 2 & 0
output: 0
总结规律:
-
任何一个数与其本身与为其本身,与0与为0
-
另外通过与运算,我们可以知道一个数是否是奇数还是偶数。
奇数 & 1 = 1
偶数 & 1 = 0
按位或(|)
运算逻辑:
1 | 1 = 1,1 | 0 = 1,0 | 1 = 1,0 | 0 = 0
注意:负数的或运算按它的补码进行运算(负数的补码 = 原码取反 + 1)
按位异或(^)
运算逻辑:
1 ^ 1 = 0,1 ^ 0 = 1, 0 ^ 1 = 1, 0 ^ 0 = 0
总结:
- 参加运算的两个数相同的话为0
- 任何一个数与0进行异或为其本身
异或满足以下几个性质:
- 交换律:a ^ b = b ^ a
- 结合律:a ^ (b ^ c) = (a ^ b) ^ c
- 自反性:a ^ b ^ b = a ^ 0 = a
- a ^ a = 0, a ^ 0 = a
同样,异或运算还能够交换两个数,再没有第三个数的情况下:
void swap(int& a, int &b)
{
a ^= b;
b ^= a;
a ^= b;
}
按位取反
运算逻辑:
~1 = 0, ~0 = 1
如果我们再IDE中编译的话,我们会发现~1 = -2, ~0 = -1。
what?这是怎么一回事,不是取反嘛,怎么会成这种结果。
其实上面的结果并没有错,我们一步一步来看。
我们假设都为八位二进制。
1: 0000 0001
取反的结果为:1111 1110,此时这个数为负数是以补码的形式保存在计算机内部中的,我们取它的原码为:1000 0010,最高位为符号位,此时为负。
所以最终结果为-2。
我们在来看负数如何取反,
例如:~-2 = 1是如何得来得
要知道计算机保存负数是保存的它的补码。
-2 :1000 0010
补码:1111 1110
此时再取反:0000 0001此时为正数
取原码:0000 0001(正数的补码=原码),所以最终结果为1
说到这,我们再来说一下负数的补码怎么计算:
X补 = ~X原 + 1 (原码取反加1,符号为不变)
正数的原码=反码=补码
左移(<<)
运算逻辑:
正数的左移,例如int32位正数,把每一位向左移动若干位,最左边的位丢弃若干位,最右边的补上若干0
负数的话,由于最高位是1,所以最高位保持不变,左边舍弃若干个,除了最高位,右边补若干个0即可
例如 a = 1000 0110(-6) a << 1 = 1000 11 00(-12)
其实我们可以发现,左移实际上就是 a * 2的左移多少位次方,
右移(>>)
运算逻辑:
正数的右移若干位,只需要左边补上若干个0,右边舍弃若干个即可。
负数的右移若干位,只需左边不上若干个1,右边舍弃若干个。
5 >> 1 = 2
5 >> 2 = 1
右移多少位,相当于除于2的多少位次方。
不过需要注意的是,我们有些编程语言处理情况不一样。
在Java中,-3 >> 1 = -2, -3 / 2 = -1
但是在Python中,-3 >> 1 = -2, -3 // 2 = -2。
不过右移运算得出的答案都遵循计算机运算规则,但是除法的话是按照解释器的处理而言。
总结
位运算到此为止,我们再写代码的时候其实可以把某些运算写成位运算更能题高效率。
-
判断x的奇偶数
x % 2 == 1 // 常规写法
x & 1 == 1 // 为真奇数,为假偶数
-
计算中位数
mid = (left + right) / 2 // 常规写法
mid = (left + right) >> 1 // 位运算
-
交换两个数
a ^= b
b ^= a
a ^= b
总结到此,欢迎补充