最近在做一个小项目是需要对某个功能进行授权,其中有 查看、编辑和下载这3种权限, 如果用简单快捷的方法那肯定是增加三个字段分别控制, 复杂一点就是利用位控制。
位运算概览
符号 | 描述 | 数学符表示 | 运算规则 |
---|---|---|---|
& | 与 | &、AND | 两个位都为1时才为1 |
| | 或 | | 、or | 两个位只要有一个为1时就为1 |
^ | 异或 | ⊕、xor | 两个位相同为0,不同为1 |
~ | 取反 | 二进制补码中的0 和 1 全部取反 | |
<< | 左移 | 二进制向左移动n 位所得的值 | |
>> | 右移 | 二进制向右移动n 位所得的值 |
上面表格中提到的补码在之前的文章有介绍,不了解的可以>>前往学习
接下来通过几个例子来加深理解
按位与运算符(&)
运算规则:
0&0=0 0&1=0 1&0=0 1&1=1
例如:
计算 6&14=6
的详细步骤如下:
6的二进制表示为: 0000 0110
14的二进制表示为: 0000 1110
6&14的结果为: 0000 0110复制
注:正数的原码、反码、补码都是一样的,负数要先转换成补码的形式参与运算
场景:
很多在初学编程的时候都写过判断奇偶数,一般的写法如下:
if n%2 == 1 {
// n 是个奇数
}复制
如果把n 以二进制的形式展示的话,只需要判断最后一个二进制位是不是1就行了,是1的话就是奇数。位运算的代码如下:
if n&1 == 1 {
// n 是奇数
}复制
按位或运算符(|)
运算规则:
0|0=0 0|1=1 1|0=1 1|1=1
例如:
计算 6|13=15
的详细步骤如下:
6的二进制表示为: 0000 0110
13的二进制表示为: 0000 1101
6&13的结果为: 0000 1111复制
注:正数的原码、反码、补码都是一样的,负数要先转换成补码的形式参与运算
场景:
用位做权限控制的时候,用来设置某个位置为1。假设我们要设置6的第4位为1,只需要和8 (二进制表示为1000
)进行或运算。
0110 | 1000 = 1110 (14)
复制
8
刚好是 2^(4-1)
, 所以我们可以总结出规律并封装一个方法
func setStatus(position, baseon int) int {
return baseon | int(math.Pow(2, float64(position-1)))
}
setStatus(4,6) // 14复制
按位异或运算符(^)
运算规则:
0^0=0 0^1=1 1^0=1 1^1=0
例如:
计算 6^14=8
的详细步骤如下:
6的二进制表示为: 0000 0110
14的二进制表示为: 0000 1110
6&14的结果为: 0000 1000复制
注:正数的原码、反码、补码都是一样的,负数要先转换成补码的形式参与运算
场景:
交换a 和b 两个数的值
传统代码:
int tmp = x;
x = y;
y = tmp;复制
能不能不定义临时变量完成?(面试PHP的时候有问到过), php有一个函数 list
可以实现这个功能:
list(x,y) = [y, x]
有没有觉得很low
? 接下来再介绍一种高逼格的写法。
x = x ^ y // (1)
y = x ^ y // (2)
x = x ^ y // (3)复制
是不是没看懂?接下来我们来分析一下。我们知道,两个相同的数 异或之后的结果为0,即 n^n=0
。任何数和0 异或等于它本身,即 0^n=n
。所以解释如下:
我们把(1)中的x带入(2)中的x
,有y=x^y=(x^y)^y=x^(y^y)= x^0=x
。x
的值成功赋给了y
。
对于(3),推导如下:x = x^y = (x^y)^x = (x^x)^y = 0^y = y
。
通过上面的列子我们发现 异或 的几条性质:
交换律 结合律: (a^b)^c == a^(b^c)
对于任何数x,都有 x^x=0,x^0=x
自反性: a^b^b=a^0=a
;
按位取反运算符(~)
运算规则:
~1=0 ~0=1
例如:
计算 ~6=-7
的详细步骤如下:
6的二进制表示为: 0000 0110
14的二进制表示为: 0000 1110
6&14的结果为: 0000 1000复制
注:正数的原码、反码、补码都是一样的,负数要先转换成补码的形式参与运算
场景:
使一个数的最低位为零
使a
的最低位为0
,可以表示为:a & ~1
。~1
的值为 1111 1111 1111 1110
,再按"与"运算,最低位一定为0
。因为" ~"运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。
左移运算符(<<)
定义:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。
设 a=1010 1110,a = a<<2
将a
的二进制位左移2位、右补0,即得a=1011 1000
。
若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。
右移运算符(>>)
定义:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
例如:a=a>>2
将a的二进制位右移2位,左补0或者左补1 要看被移数是正还是负。
操作数每右移一位,相当于该数除以2。