暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

学好位运算,这篇就足够了

技术账本 2021-10-29
399


最近在做一个小项目是需要对某个功能进行授权,其中有 查看、编辑和下载这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。



文章转载自技术账本,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论