位运算的几个常用例子
众所周知,在代码中使用位运算能大幅提高运算效率。现代计算机由于性能提升明显、一些编译器也会对此做优化。但是对于一些功能要求高效率的时候,尤其是底层常用的函数体,位运算是一个比较好的选择。例如:golang hashmap的实现就大幅运用了位运算。接下来简单介绍几个常用的位运算。
- 判断奇偶(工程中做ab实验的时候会经常用到) 1的二进制为 0001,正数奇数最后一位为1,所以和1与 得到的为1。在讲负数的奇偶性判断前,先说下负数的二进制表示:
1
2
3
4
5
6var a,b int8
a = 7
b = 0
//判断奇偶
fmt.Println(a&1)
fmt.Println(b&1)对于负数x,最左边一位为符号位1,先计算出它的正数的二进制补码,然后+1 得到负数的二进制码。
举个例子:对于int8的 -5,求二进制码。 - 5的二进制码为:0000 0101
- 求5的补码:1111 1010
- 补码+1,得到-5的二进制码:1111 1011
1的二进制表示为:0000 0001,-5&1 => 0000 0001,所以奇偶性判断方法,对于负数也是适用的。
在介绍乘除2功能前,说下算术移和逻辑移的区别。二者都是左右移动位数。但是在有无符号上有明显的不同。在无符号的情况下,二者是没有区别的,左移都是舍弃最左边的编码,最后面补0;右移都是最高位补0,舍弃最右边编码。在有符号的情况下,算术移要保持最左边的位码不变,逻辑移规则和无符号一致。
特别说明:golang里的左右移都是算术移。
- 乘除 2
- 乘以2 对于8的二进制表示为:0000 1000,左移一位为:0001 0000,得到16。
1
2
3var d uint8
d = 8
fmt.Println(d << 1) - 除以 2 特别说下有符号的情况下,右移的情况: -5 >> 1
1
2
3
4var d uint8
d = 10
fmt.Println(d >> 1)
fmt.Println("=================")
首先,我们得到-5的二进制码:1111 1011。保持最左的符号位不变,右移一位,得到:1111 1101。
逆向求其正数:- 减1,得到:1111 1100
- 求补码:0000 0011,也就是3
所以,得到 -5 >> 1 = -3
- 取整数最大值
这是一个比较巧妙的例子,实际工程中可能用的不是太多。
1
2const MaxUint = ^uint(0)
fmt.Println(MaxUint)
4. 加密、解密
待补
[参考资料]