Go 语言运算符

位运算

位运算只能用于整数类型的变量,且需当它们拥有等长位模式时。%b 是用于表示位的格式化标识符。

二元运算符

按位与 &

  1. 1 & 1 -> 1
  2. 1 & 0 -> 0
  3. 0 & 1 -> 0
  4. 0 & 0 -> 0

按位或 |

  1. 1 | 1 -> 1
  2. 1 | 0 -> 1
  3. 0 | 1 -> 1
  4. 0 | 0 -> 0

按位异或 ^

  1. 1 ^ 1 -> 0
  2. 1 ^ 0 -> 1
  3. 0 ^ 1 -> 1
  4. 0 ^ 0 -> 0

以上示例中,为了方便理解,可以将1看作true0看作false

位清除 &^:将指定位置上的值设置为 0。

一元运算符

按位补足 ^

该运算符与异或运算符一同使用,即 m^x,对于无符号 x 使用“全部位设置为 1”,对于有符号 x 时使用 m=-1。例如:

  1. ^10 = -01 ^ 10 = -11

位左移 <<

用法:bitP << n

bitP 的位向左移动 n 位,右侧空白部分使用 0 填充;如果 n 等于 2,则结果是 2 的相应倍数,即 2n 次方。例如:

  1. 1 << 10 // 等于 1 KB
  2. 1 << 20 // 等于 1 MB
  3. 1 << 30 // 等于 1 GB

位右移 >>

用法:bitP >> nbitP 的位向右移动 n 位,左侧空白部分使用 0 填充;如果 n 等于 2,则结果是当前值除以 2n 次方。当希望把结果赋值给第一个操作数时,可以简写为 a <<= 2 或者 b ^= a & 0xffffffff

位左移常见实现存储单位的用例

使用位左移与 iota 计数配合可优雅地实现存储单位的常量枚举:

  1. type ByteSize float64
  2. const (
  3. _ = iota // 通过赋值给空白标识符来忽略值
  4. KB ByteSize = 1<<(10*iota)
  5. MB
  6. GB
  7. TB
  8. PB
  9. EB
  10. ZB
  11. YB
  12. )

在通讯中使用位左移表示标识的用例

  1. type BitFlag int
  2. const (
  3. Active BitFlag = 1 << iota // 1 << 0 == 1
  4. Send // 1 << 1 == 2
  5. Receive // 1 << 2 == 4
  6. )
  7. flag := Active | Send // == 3

逻辑运算符

Go 中拥有以下逻辑运算符:==、!=、<、<=、>、>=。

它们之所以被称为逻辑运算符是因为它们的运算结果总是为布尔值 bool。例如:

  1. b3 := 10 > 5 // b3 is true

算术运算符

常见可用于整数和浮点数的二元运算符有 +、-、*、/

/ 对于整数运算而言,结果依旧为整数,例如:9 / 4 -> 2

取余运算符只能作用于整数:9 % 4 -> 1。

整数除以 0 可能导致程序崩溃,将会导致运行时的恐慌状态(如果除以 0 的行为在编译时就能被捕捉到,则会引发编译错误);第 13 章将会详细讲解如何正确地处理此类情况。

浮点数除以 0.0 会返回一个无穷尽的结果,使用 +Inf 表示。

你可以将语句 b = b + a 简写为 b+=a,同样的写法也可用于 -=、*=、/=、%=

对于整数和浮点数,你可以使用一元运算符 ++(递增)和 --(递减),但只能用于后缀:

  1. i++ -> i += 1 -> i = i + 1
  2. i-- -> i -= 1 -> i = i - 1

同时,带有 ++-- 的只能作为语句,而非表达式,因此 n = i++ 这种写法是无效的,其它像 f(i++) 或者 a[i]=b[i++] 这些可以用于 C、C++Java 中的写法在 Go 中也是不允许的。

在运算时 溢出 不会产生错误,Go 会简单地将超出位数抛弃。如果你需要范围无限大的整数或者有理数(意味着只被限制于计算机内存),你可以使用标准库中的 big 包,该包提供了类似 big.Intbig.Rat这样的类型。

  1. package main
  2. import "fmt"
  3. func main() {
  4. var a int = 21
  5. var b int = 10
  6. var c int
  7. c = a + b
  8. fmt.Printf("第一行 - c 的值为 %d\n", c )
  9. c = a - b
  10. fmt.Printf("第二行 - c 的值为 %d\n", c )
  11. c = a * b
  12. fmt.Printf("第三行 - c 的值为 %d\n", c )
  13. c = a / b
  14. fmt.Printf("第四行 - c 的值为 %d\n", c )
  15. c = a % b
  16. fmt.Printf("第五行 - c 的值为 %d\n", c )
  17. a++
  18. fmt.Printf("第六行 - a 的值为 %d\n", a )
  19. a=21 // 为了方便测试,a 这里重新赋值为 21
  20. a--
  21. fmt.Printf("第七行 - a 的值为 %d\n", a )
  22. }

输出结果:

  1. 第一行 - c 的值为 31
  2. 第二行 - c 的值为 11
  3. 第三行 - c 的值为 210
  4. 第四行 - c 的值为 2
  5. 第五行 - c 的值为 1
  6. 第六行 - a 的值为 22
  7. 第七行 - a 的值为 20

随机数

一些像游戏或者统计学类的应用需要用到随机数。rand 包实现了伪随机数的生成。

示例,生成 10 个非负随机数:

  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "time"
  6. )
  7. func main() {
  8. for i := 0; i < 10; i++ {
  9. a := rand.Int()
  10. fmt.Printf("%d / ", a)
  11. }
  12. for i := 0; i < 5; i++ {
  13. r := rand.Intn(8)
  14. fmt.Printf("%d / ", r)
  15. }
  16. fmt.Println()
  17. timens := int64(time.Now().Nanosecond())
  18. rand.Seed(timens)
  19. for i := 0; i < 10; i++ {
  20. fmt.Printf("%2.2f / ", 100*rand.Float32())
  21. }
  22. }

可能的输出:

  1. 816681689 / 1325201247 / 623951027 / 478285186 / 1654146165 /
  2. 1951252986 / 2029250107 / 762911244 / 1372544545 / 591415086 / / 3 / 0 / 6 / 4 / 2 /22.10
  3. / 65.77 / 65.89 / 16.85 / 75.56 / 46.90 / 55.24 / 55.95 / 25.58 / 70.61 /

函数 rand.Float32rand.Float64 返回介于 [0.0, 1.0) 之间的伪随机数,其中包括 0.0 但不包括 1.0。函数 rand.Intn 返回介于 [0, n) 之间的伪随机数。

你可以使用 rand.Seed(value) 函数来提供伪随机数的生成种子,一般情况下都会使用当前时间的纳秒级数字。

运算符与优先级

有些运算符拥有较高的优先级,二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低:

优先级 运算符
7 ^ !
6 * / % << >> & &^
5 + - | ^
4 == != < <= >= >
3 <-
2 &&
1 ||

当然,你可以通过使用括号来临时提升某个表达式的整体运算优先级。