Bitwise Operation
Posted on
May 22, 2018
在计算机刚出现时的远古年代(其实也就几十年), CPU ,内存和网络带宽都是非常珍贵的资源,在处理或者发送数据时,为了尽可能地节约资源,程序员都需要尽可能控制数据的大小,例如布尔值能用一个比特表示就绝不使用一个字节。
一般网络协议的头部通过指定特定比特来表示特定的意思,还有在很多 low-level
的系统编程中,位运算也非常常见。所以,位运算可以说是程序员的必备技能了,下面让我们来看几种基本的位运算吧。
1 2 3 4 5 6 7 8 9 10 11 type op struct {}type BitwiseOperation interface { AND(byte , byte ) byte ANDNOT(byte , byte ) byte OR(byte , byte ) byte XOR(byte . byte ) byte NOT(byte ) byte SHIFT_LEFT(byte , uint ) byte SHIFT_RIGHT(byte , uint ) byte }
& (AND) AND
运算符可以有选择性地将数的某位清零,除此之外,还可以检测数的奇偶性(1 & a == 1)。
1 2 3 4 0xAC // 10101100 & 0xF0 // 11110000 = 0xA0 // 10100000 ^ ^
1 2 3 func (op *bitwiseOp) AND(x byte, y byte) byte { return x & y }
&^ (AND NOT) 与 AND
类似,AND NOT
也可以将特定的位清0,但是 AND NOT
的语义更加符合正常思维,写起来也更加直观。
1 2 3 4 0xAB // 10101011 &^ 0x03 // 00000011 = 0xA8 // 10101000 ^ ^ ^
1 2 3 func (op *op) ANDNOT (x byte , y byte ) byte { return x &^ y }
| (OR) OR
操作可以有选择性地将一个字节的某几个比特设为1。
1 2 3 4 0x00 // 00000000 | 0xC4 // 11000100 = 0xC4 // 11000100 ^^ ^
1 2 3 func (op *op) OR (x byte , y byte ) byte { return x | y }
^ (XOR | NOT) Binary ^ (XOR) 使用 XOR
可以将位从一个值切换到另一个(toggle),除此之外,XOR
还可以用于比较两个数的符号是否相同。当 (a ^ b) ≥ 0
时,表示两个整数a,b具有相同的符号(或当 (a ^ b) < 0
时表示符号相反)。
1 2 3 4 0xCEFF // 1100111011111111 ^ 0xFF00 // 1111111100000000 = 0x31FF // 0011000111111111 ^^ ^^^^^^^^^
1 2 3 func (op *op) XOR (x byte , y byte ) byte { return x ^ y }
Unary ^ (NOT) 与其他语言(c / c ++,Java,Python,Javascript等)不同,Go没有专门的取反位运算符,但它可以用 ^
来表示 NOT
, 也就是按位取反。
1 2 3 ^ 0x0F // 00001111 = 0xF0 // 11110000 ^^^^
1 2 3 func (op *op) NOT (x byte ) byte { return ^x }
SHIFT Shift
操作可以和 &
|
&^
这三个操作符组合起来使用。
|
与 <<
运算符组合可以用于设置a中的第n位。
或者将 &
和 <<
运算符组合来测试第n位是否被设置。
使用 &^
和 <<
,我们可以取消设置值的第n位。
使用左右移位运算符还可以完成效率更高的乘法和除法。
>> (Shift Right) 1 2 3 4 5 6 7 8 0x78 // 01111000 ^^^^ >> 1 0x3C // 00111100 ^^^^ >> 2 0x1E // 00011110 ^^^^ >> 3 0x0F // 00001111 ^^^^
1 2 3 func (op *op) SHIFT_RIGHT (x byte , n uint ) byte { return x >> n }
<< (Shift Left) 1 2 3 4 5 6 7 8 0x03 // 00000011 ^^ << 1 0x60 // 00000110 ^^ << 2 0xC0 // 00001100 ^^ << 3 0x18 // 00011000 ^^
1 2 3 func (op *op) SHIFT_LEFT (x byte , n uint ) byte { return x << n }
bitset 了解了基本的位操作后,我们可以用它来实现 bitset
,使用 bitset
存储布尔值相对 map[uint]bool
来说更加高效。 bitset
可以在很多场景下使用,比方说你是一个旅店老板,就可以使用 bitset
来存储每个房间的状态,如果房间被占用了,则将相应的比特位设置为1,如果房间为空,则相应的比特位设置为0。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 uint64 +----------------------------------------------------------------+ |................x................................x..............| +----------------------------------------------------------------+ uint64 +----------------------------------------------------------------+ |...................................x............................| +----------------------------------------------------------------+ uint64 +----------------------------------------------------------------+ |...............x................................................| +----------------------------------------------------------------+ uint64 +----------------------------------------------------------------+ |...................x..........x..........x......................| +----------------------------------------------------------------+
1 2 3 4 5 6 7 8 9 10 type bitset []uint64 func newBitset (bits uint ) bitset { extra := uint (0 ) if bits % 64 != 0 { extra = 1 } chunks := bits/64 + extra return bitset(make ([]uint64 , chunks)) }
1 2 3 func bitsetIndex (pos uint ) (uint , uint ) { return pos / 64 , pos % 64 }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func (b bitset) set (pos uint ) bitset { major, minor := bitsetIndex(pos) b[major] |= (1 << minor) return b } func (b bitset) clear (pos uint ) bitset { major, minor := bitsetIndex(pos) b[major] &^= (1 << minor) return b } func (b bitset) get (pos uint ) bool { major, minor := bitsetIndex(pos) return b[major] & (1 << minor) != 0 }
1 2 3 4 5 6 7 8 9 10 11 func (b bitset) popcnt () uint { total := uint (0 ) for _, v := range b { v = (v&0x5555555555555555 ) + ((v&0xAAAAAAAAAAAAAAAA ) >> 1 ) v = (v&0x3333333333333333 ) + ((v&0xCCCCCCCCCCCCCCCC ) >> 2 ) v = (v&0x0F0F0F0F0F0F0F0F ) + ((v&0xF0F0F0F0F0F0F0F0 ) >> 4 ) v *= 0x0101010101010101 total += uint ((v >> 56 ) & 0xFF ) } return total }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 func (b bitset) clone () bitset { dataCopy := make ([]uint64 , len (b)) copy (dataCopy, b) return bitset(dataCopy) } func (b bitset) hash () uint64 { hash := uint64 (b.popcnt()) for _, v := range b { hash ^= v } return hash } func (b bitset) equals (b2 bitset) bool { if len (b) != len (b2) { return false } for i := range b { if b[i] != b2[i] { return false } } return true }