在计算机内存昂贵,处理能力有限的美好旧时光里,用比较黑客范的位运算方式去处理信息是首选方式(某些情况下只能如此)。时至今日,直接使用位运算仍然是很多计算领域中不可或缺的部分,例如底层系统编程,图形处理,密码学等。
Go 编程语言支持以下按位运算符:
& bitwise AND
| bitwise OR
^ bitwise XOR
&^ AND NOT
<< left shift
>> right shift
本文的余下部分详述了每个操作符以及它们如何使用的案例。
& 运算符
&ANDAND
Given operands a, b
AND(a, b) = 1; only if a = b = 1
else = 0
AND&
func main() {
var x uint8 = 0xAC // x = 10101100
x = x & 0xF0 // x = 10100000
}
所有的位运算都支持简写的赋值形式。 例如,前面的例子可以重写为如下。
func main() {
var x uint8 = 0xAC // x = 10101100
x &= 0xF0 // x = 10100000
}
&&AND
import (
"fmt"
"math/rand"
)
func main() {
for x := 0; x < 100; x++ {
num := rand.Int()
if num&1 == 1 {
fmt.Printf("%d is odd\n", num)
} else {
fmt.Printf("%d is even\n", num)
}
}
}
在 Playground 上运行上面的例子
| 操作符
|或或
Given operands a, b
OR(a, b) = 1; when a = 1 or b = 1
else = 0
或或
func main() {
var a uint8 = 0
a |= 196
fmt.Printf("%b", a)
}
// 打印结果 11000100
^^ ^
练习场中可运行范例。
或a
func main() {
var a uint8 = 0
a |= 196
a |= 3
fmt.Printf("%b", a)
}
// 打印结果 11000111
在练习场中可以运行范例。
或
位运算的配置用法
AND(a, 1) = a 当且仅当 a = 1a & 196aORAND
procstrstrconf
const (
UPPER = 1 // 大写字符串
LOWER = 2 // 小写字符串
CAP = 4 // 字符串单词首字母大写
REV = 8 // 反转字符串
)
func main() {
fmt.Println(procstr("HELLO PEOPLE!", LOWER|REV|CAP))
}
func procstr(str string, conf byte) string {
// 反转字符串
rev := func(s string) string {
runes := []rune(s)
n := len(runes)
for i := 0; i < n/2; i++ {
runes[i], runes[n-1-i] = runes[n-1-i], runes[i]
}
return string(runes)
}
// 查询配置中的位操作
if (conf & UPPER) != 0 {
str = strings.ToUpper(str)
}
if (conf & LOWER) != 0 {
str = strings.ToLower(str)
}
if (conf & CAP) != 0 {
str = strings.Title(str)
}
if (conf & REV) != 0 {
str = rev(str)
}
return str
}
在 Playground上面运行代码.
procstr("HELLO PEOPLE!", LOWER|REV|CAP)conf
^ 操作符
异或^异或
Given operands a, b
XOR(a, b) = 1; only if a != b
else = 0
异或
func main() {
var a uint16 = 0xCEFF
a ^= 0xFF00 // same a = a ^ 0xFF00
}
// a = 0xCEFF (11001110 11111111)
// a ^=0xFF00 (00110001 11111111)
异或异或(a ^ b) ≥ 0(a ^ b) < 0true
func main() {
a, b := -12, 25
fmt.Println("a and b have same sign?", (a ^ b) >= 0)
}
在 Go 的 Playground运行代码。
a and b have same sign? false
^ 作为取反位运算符 (非)
XOR^^aa
func main() {
var a byte = 0x0F
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", ^a)
}
// 打印结果
00001111 // var a
11110000 // ^a
在练习场中可以运行范例。
&^ 操作符
&^与非与非
Given operands a, b
AND_NOT(a, b) = AND(a, NOT(b))
如果第二个操作数为 1 那么它则具有清除第一个操作数中的位的趣味特性。
AND_NOT(a, 1) = 0; clears a
AND_NOT(a, 0) = a;
AND NOT1010 10111010 0000
func main() {
var a byte = 0xAB
fmt.Printf("%08b\n", a)
a &^= 0x0F
fmt.Printf("%08b\n", a)
}
// 打印:
10101011
10100000
在练习场中运行范例。
<< 和 >> 操作符
<<>>
Given integer operands a and n,
a << n; shifts all bits in a to the left n times
a >> n; shifts all bits in a to the right n times
a00000011
func main() {
var a int8 = 3
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", a<<1)
fmt.Printf("%08b\n", a<<2)
fmt.Printf("%08b\n", a<<3)
}
// 输出的结果:
00000011
00000110
00001100
00011000
在 Playground 运行代码
注意每次移动都会将低位右侧补零。相对应,使用右移位操作符进行运算时,每个位均向右方移动,空出的高位补零,如下示例 (有符号数除外,参考下面的算术移位注释)。
func main() {
var a uint8 = 120
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", a>>1)
fmt.Printf("%08b\n", a>>2)
}
// 打印:
01111000
00111100
00011110
在 练习场中可以运行范例。
200
func main() {
a := 200
fmt.Printf("%d\n", a>>1)
}
// 打印:
100
在 练习场 中可以运行范例。
或是通过左移 2 位,将一个数乘以4:
func main() {
a := 12
fmt.Printf("%d\n", a<<2)
}
// 打印:
48
在 练习场 中可以运行范例。
|<
func main() {
var a int8 = 8
fmt.Printf("%08b\n", a)
a = a | (1<<2)
fmt.Printf("%08b\n", a)
}
// prints:
00001000
00001100
可以在 练习场 中运行代码示例。
&
func main() {
var a int8 = 12
if a&(1<<2) != 0 {
fmt.Println("take action")
}
}
// 打印:
take action
在 练习场中运行代码。
&^
func main() {
var a int8 = 13
fmt.Printf("%04b\n", a)
a = a &^ (1 << 2)
fmt.Printf("%04b\n", a)
}
// 打印:
1101
1001
在 练习场 中运行代码。
关于算术位移运算的笔记
当要位移的值(左操作数)是有符号值时,Go 自动应用算术位移。在右移操作期间,复制(或扩展)二进制补码符号位以填充位移的空隙。
总结
与其它现代运算符一样,Go 支持所有二进制位操作运算符。这篇文章仅仅提供了可以用这些操作符完成的各种黑科技示例。你可以在网络上找到很多文章,特别是 Sean Eron Anderson 写的 Bit Twiddling Hacks 。
关注 Vladim @vladimirvivien 的 Twitter。
如果你正在学习 Go,阅读 Vladimir Vivien 关于 Go 的书,名为 Learning Go Programming 。
这篇文章开始由作者 Vladimir Vivien 发布在 Medium 上,名为 Bit Hacking with Go。
译文地址:https://learnku.com/go/t/23460/bit-operation-of-go