#《Go 语音圣经(中文版)》

#基础数据类型

Go语言将数据类型分为四类:基础类型复合类型引用类型接口类型

#整型

int8int16int32int64四种不同大小的有符号整数类型,分别对应8163264bit 大小的有符号整数,与此对应的是uint8uint16uint32uint64四种无符号整数类型。

还有两种一般对应特定 CPU 平台机器字大小的有符号和无符号整数intuint。其中int是应用最广泛的数值类型。这两种类型都有同样的大小,3264bit,但是我们不能对此做任何的假设,因为不同的编译器即使在相同的硬件平台上可能产生不同的大小

Unicode 字符rune类型是和int32等价的类型,通常用于表示一个 Unicode 码点。这两个名称可以互换使用。同样byte也是uint8类型的等价类型,byte类型一般用于强调数值是一个原始的数据而不是一个小的整数。

还有一种无符号的整数类型uintptr,没有指定具体的 bit 大小但是足以容纳指针。uintptr类型只有在底层编程时才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地 方。

二元运算符及优先级:

1
2
3
4
5
*	/	%	<<	>>	&	&^
+ - | ^
== != < <= > >=
&&
||

二元运算符有五种优先级。在同一个优先级,使用左优先结合规则,但是使用括号可以明确优先顺序,使用括号也可以用于提升优先级。

Go语言中,%取模运算符的符号和被取模数的符号总是一致的,因此-5%3-5%-3结果都是-2

整数除法会向着0方向截断余数。

布尔型数字类型字符串等基本类型都是可比较的。

1
2
3
4
5
6
&	位运算 AND
| 位运算 OR
^ 位运算 XOR
&^ 位清空 (AND NOT)
<< 左移
>> 右移

前面四个操作运算符并不区分是有符号还是无符号数。

位操作运算符&^用于按位置零:如果对应ybit位为1的话,表达式z = x &^ y结果z的对应的bit位为0,否则z对应的bit位等于x相应的bit位的值。

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
26
27
28
29
package main

import "fmt"

func main() {

var x uint8 = 1<<1 | 1<<5
var y uint8 = 1<<1 | 1<<2
var z int8 = 1<<1 | 1<<5

fmt.Println(x, y, z)
fmt.Printf("%08b\n", x)
fmt.Printf("%08b\n", y)
fmt.Printf("%08b\n", z)

fmt.Printf("%08b\n", x&y)
fmt.Printf("%08b\n", x|y)
fmt.Printf("%08b\n", x^y)
fmt.Printf("%08b\n", x&^y)

for i := uint(0); i < 8; i++ {
if x&(1<<i) != 0 {
fmt.Println(i)
}
}

fmt.Printf("%08b\n", x<<1)
fmt.Printf("%08b\n", x>>1)
}

结果:

1
2
3
4
5
6
7
8
9
10
11
12
34 6 34
00100010
00000110
00100010
00000010
00100110
00100100
00100000
1
5
01000100
00010001

左移运算用零填充右边空缺的 bit 位,无符号数的右移运算也是用 0 填充左边空缺的 bit 位,但是有符号数的右移运算会用符号位的值填充左边空缺的 bit 位。

浮点数到整数的转换将丢失任何小数部分,然后向数轴零方向截断。

通常Printf格式化字符串包含多个%参数时将会包含对应相同数量的额外操作数,但是%之后的[1]副词告诉Printf函数再次使用第一个操作数。%
#副词告诉Printf在用%o%x%X输出时生成00x0X前缀。

1
2
3
4
o := 0666
fmt.Printf("%d %[1]o %#[1]o\n", o)
x := int64(0xdeadbeef)
fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x)

结果如下:

1
2
438 666 0666
3735928559 deadbeef 0xdeadbeef 0XDEADBEEF

字符使用%c参数打印,或者是用%q参数打印带单引号的字符:

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
ascii := 'a'
unicode := '国'
newline := '\n'
fmt.Printf("%d %[1]c %[1]q\n", ascii) // "97 a 'a'"
fmt.Printf("%d %[1]c %[1]q\n", unicode) // "22269 国 '国'"
fmt.Printf("%d %[1]q\n", newline) // "10 '\n'"
}
1
2
3
97 a 'a'
22269 国 '国'
10 '\n'

#浮点数

一个float32类型的浮点数可以提供大约6个十进制数的精度,而float64则可以提供约15个十进制数的精度。

小数点前面或后面的数字都可能被省略,例如.7071.。很小或很大的数最好用科学计数法书写,通过eE来指定指数部分:

1
2
const Avogadro = 6.02214129e23	// 阿伏伽德罗常数
const Planck = 6.62606957e-34 // 普朗克常数

Printf函数的%g参数打印浮点数,将采用更紧凑的表示形式打印,并提供足够的精度,但是对应表格的数据,使用%e(带指数)或%f的形式打印可能更合适。所有的这三个打印形式都可以指定打印的宽度和控制打印精度。

1
2
3
4
5
6
7
8
9
10
11
12
package main

import (
"fmt"
"math"
)

func main() {
for x := 0; x < 8; x++ {
fmt.Printf("x = %d e^x = %8.3f %8.3[2]e\n", x, math.Exp(float64(x)))
}
}

打印精度是小数点后三个小数精度和8个字符宽度:

1
2
3
4
5
6
7
8
x = 0 e^x =    1.000 1.000e+00
x = 1 e^x = 2.718 2.718e+00
x = 2 e^x = 7.389 7.389e+00
x = 3 e^x = 20.086 2.009e+01
x = 4 e^x = 54.598 5.460e+01
x = 5 e^x = 148.413 1.484e+02
x = 6 e^x = 403.429 4.034e+02
x = 7 e^x = 1096.633 1.097e+03

正无穷大:表示太大溢出的数字。
负无穷大:表示除零的结果。
NAN:非数,一般用于表示无效的除法操作结果0/0Sqrt(-1)

1
2
3
4
5
6
7
8
9
10
package main

import (
"fmt"
)

func main() {
var z float64
fmt.Println(z, -z, 1/z, -1/z, z/z)
}
1
0 -0 +Inf -Inf NaN

函数math.IsNaN用于测试一个数是否是非数NaN

测试一个结果是否是非数NaN则是充满风险的,因为NaN和任何数都是不相等的。[1]

如果一个函数返回的浮点数结果可能失败,最好的做法是用单独的标志报告失败,像这样:[2]

1
2
3
4
5
6
7
func compute() (value float64, ok bool) {
// ...
if failed {
return 0, false
}
return result, true
}
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// See page 58.
//!+

// Surface computes an SVG rendering of a 3-D surface function.
package main

import (
"fmt"
"math"
)

const (
width, height = 600, 320 // canvas size in pixels
cells = 100 // number of grid cells
xyrange = 30.0 // axis ranges (-xyrange..+xyrange)
xyscale = width / 2 / xyrange // pixels per x or y unit
zscale = height * 0.4 // pixels per z unit
angle = math.Pi / 6 // angle of x, y axes (=30°)
)

var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)

func main() {
fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' "+
"style='stroke: grey; fill: white; stroke-width: 0.7' "+
"width='%d' height='%d'>", width, height)
for i := 0; i < cells; i++ {
for j := 0; j < cells; j++ {
ax, ay := corner(i+1, j)
bx, by := corner(i, j)
cx, cy := corner(i, j+1)
dx, dy := corner(i+1, j+1)
fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",
ax, ay, bx, by, cx, cy, dx, dy)
}
}
fmt.Println("</svg>")
}

func corner(i, j int) (float64, float64) {
// Find point (x,y) at corner of cell (i,j).
x := xyrange * (float64(i)/cells - 0.5)
y := xyrange * (float64(j)/cells - 0.5)

// Compute surface height z.
z := f(x, y)

// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).
sx := width/2 + (x-y)*cos30*xyscale
sy := height/2 + (x+y)*sin30*xyscale - z*zscale
return sx, sy
}

func f(x, y float64) float64 {
r := math.Hypot(x, y) // distance from (0,0)
return math.Sin(r) / r
}

//!-

渲染结果图

94/483


  1. 在浮点数中,NaN、正无穷大和负无穷大都不是唯一 的,每个都有非常多种的bit模式表示。 ↩︎

  2. 似懂非懂,有机会再补这个的例子吧! ↩︎