介绍
欢迎 Gophers 来到这个新章节。在本节中,我们将学习数值类型。内容包括常用的整数类型、浮点数类型、布尔类型,以及复数类型和 Go 1.13 版本中引入的字面值语法。
知识点:
- 整数类型
- 浮点数类型
- 布尔类型
- 复数类型
- 字面值语法
欢迎 Gophers 来到这个新章节。在本节中,我们将学习数值类型。内容包括常用的整数类型、浮点数类型、布尔类型,以及复数类型和 Go 1.13 版本中引入的字面值语法。
知识点:
整数可以大致分为两类:无符号整数(unsigned integers)和有符号整数(signed integers)。其中,有符号整数是最广泛使用的。
无符号整数只能表示非负数(0 和正数),而有符号整数可以表示负数和非负数。
无符号整数可以分为四种大小:8 位、16 位、32 位和 64 位,分别用 uint8
、uint16
、uint32
和 uint64
表示。对应的有符号整数为 int8
、int16
、int32
和 int64
。下表展示了每种类型表示的不同范围:
类型 | 描述 | 范围 |
---|---|---|
uint8 | 8 位无符号整数 | 0 到 255 |
int8 | 8 位有符号整数 | -128 到 127 |
uint16 | 16 位无符号整数 | 0 到 65535 |
int16 | 16 位有符号整数 | -32768 到 32767 |
uint32 | 32 位无符号整数 | 0 到 4294967295 |
int32 | 32 位有符号整数 | -2147483648 到 2147483647 |
uint64 | 64 位无符号整数 | 0 到 18446744073709551615 |
int64 | 64 位有符号整数 | -9223372036854775808 到 9223372036854775807 |
以 uint8
和 int8
为例,它们都是 8 位整数,可以表示 256 个值。在无符号整数类型 uint8
中,其表示的范围是 0 到 255,而在有符号整数类型 int8
中,其表示的范围是 -128 到 127。
除了上述 8 种类型外,还有三种特殊的整数类型:uint
、int
和 uintptr
。其中,uint
和 int
在不同平台上可能表示不同的范围,而 uintptr
可以用于存储指针。
类型 | 范围 |
---|---|
uint |
在 32 位系统上为 uint32 ,在 64 位系统上为 uint64 |
int |
在 32 位系统上为 int32 ,在 64 位系统上为 int64 |
uintptr |
用于存储指针的无符号整数类型 |
现在,我们创建一个名为 integer.go
的文件来演示整数的使用:
cd ~/project
touch integer.go
package main
import (
"fmt"
"unsafe"
)
func main() {
// 查看当前环境下 int 的类型
// 声明 a 为 int 类型
var a int
// 使用 unsafe.Sizeof() 输出该类型占用的内存大小
fmt.Printf("当前环境下 int 类型为 %d 位\n", unsafe.Sizeof(a)*8)
var b int8 = 125
// 在 fmt.Printf 中使用 %d 占位符输出整数值
// 在 fmt.Printf 中使用 %T 占位符输出变量类型
fmt.Printf("b 的值为 %d,类型为 %T\n", b, b)
// 整数运算
// 声明整数 c 和 d,并计算它们的和
c, d := 2, 3
fmt.Printf("c + d = %d\n", c+d)
// 10 - 5
fmt.Printf("10 - 5 = %d\n", 10-5)
// 8 * 10
fmt.Printf("8 * 10 = %d\n", 8*10)
}
执行程序后,我们得到以下输出:
go run integer.go
当前环境下 int 类型为 64 位
b 的值为 125,类型为 int8
c + d = 5
10 - 5 = 5
8 * 10 = 80
输出解释:
当前环境下 int 类型为 64 位
:这一行显示运行代码的系统上 int
类型为 64 位(即 8 字节),表明它是 int64
。unsafe.Sizeof(a)
返回变量 a
占用的字节数,乘以 8 转换为位。这意味着 int
类型可以存储更大的整数值。b 的值为 125,类型为 int8
:这里我们声明了一个 int8
类型的变量 b
并赋值为 125。输出确认了这一点,显示了值和数据类型。c + d = 5
、10 - 5 = 5
、8 * 10 = 80
:这些行展示了基本的整数算术运算:加法、减法和乘法。输出确认了这些计算的结果。在这个文件中,unsafe.Sizeof()
函数可以用于获取当前变量类型占用的字节数。1 字节(byte)等于 8 位,因此 unsafe.Sizeof()*8
可以获取该类型占用的位数。从输出中可以看出,在线环境是 64 位的。在线环境中 int
的实际类型是 int64
。
我们可以在终端中使用以下命令来确定当前系统的架构:
dpkg --print-architecture
amd64
浮点数(Floating-point numbers),也称为小数,在 Go 中可以使用两种浮点类型表示:float32
和 float64
。默认的浮点类型是 float64
。
float32
和 float64
表示不同的精度。float64
的默认精度高于 float32
。
IEEE 754 标准是计算机中最广泛使用的浮点数计算标准,现代 CPU 都支持这一标准。与其他编程语言一样,Go 也使用 IEEE 754 来存储浮点数。
我们知道,计算机中的一个字节可以存储 8 位。float32
是单精度浮点数,占用 4 个字节,即 32 位。float64
是双精度浮点数,占用 8 个字节,即 64 位。
在 float32
中,符号位占 1 位,指数占 8 位,剩余的 23 位用于表示尾数。
在 float64
中,符号位同样占 1 位,指数占 11 位,剩余的 52 位用于表示尾数。
float32
可以表示的最大值约为科学计数法中的 3.4e+38,最小值约为 1.4e-45。float64
可以表示的最大值约为 1.8e+308,最小值约为 4.9e-324。我们可以看到,浮点数的范围可以从非常小到非常大。
我们可以使用常量 math.MaxFloat32
来表示 float32
的最大值,使用常量 math.MaxFloat64
来表示 float64
的最大值。
在输出浮点数时,我们可以使用 fmt
包的 Printf
函数的 %f
占位符。以下是一个示例:
cd ~/project
touch float.go
package main
import (
"fmt"
"math"
)
func main() {
// 输出不带指数形式的浮点数
fmt.Printf("2.333 不带指数形式: %f\n", 2.333)
fmt.Printf("Pi 不带指数形式: %f\n", math.Pi)
// 使用 %.2f 保留 Pi 的两位小数
fmt.Printf("Pi 保留两位小数: %.2f\n", math.Pi)
fmt.Printf("float32 的最大值: %f\n", math.MaxFloat32)
// 指数形式
fmt.Printf("2.333 的指数形式: %e", 2.333)
}
运行命令后,你将看到以下输出。
go run float.go
2.333 不带指数形式: 2.333000
Pi 不带指数形式: 3.141593
Pi 保留两位小数: 3.14
float32 的最大值: 340282346638528859811704183484516925440.000000
2.333 的指数形式: 2.333000e+00
输出解释:
2.333 不带指数形式: 2.333000
:这显示了使用 %f
占位符打印的数字 2.333
。默认情况下,%f
占位符显示小数点后 6 位,因此 2.333
变为 2.333000
。Pi 不带指数形式: 3.141593
:这行打印了常量 math.Pi
的值,它是数学常数 π 的近似值。%f
占位符以完整精度显示它。Pi 保留两位小数: 3.14
:通过使用 %.2f
,我们告诉 fmt.Printf
将数字格式化为小数点后两位,结果为 3.14
。当你只需要输出一定精度时,这非常有用。float32 的最大值: 340282346638528859811704183484516925440.000000
:这显示了 float32
可以表示的最大值。注意,这是一个非常大的数字,使用 %f
打印时,会显示许多位数。2.333 的指数形式: 2.333000e+00
:这行展示了如何使用 %e
占位符以指数(科学计数法)形式表示浮点数。末尾的 e+00
表示我们将 2.333000
乘以 10 的 0 次方,即 2.333
。如果指数是 e+02
,则数字将乘以 10^2
(100),结果为 233.3
。bool
类型只有两个可能的值:true
或 false
,其中 false
是默认值。它具有以下特点:
布尔类型通常与关系运算符一起使用,例如 =
、>
和 <
。让我们创建一个名为 bool.go
的文件来查看演示:
cd ~/project
touch bool.go
package main
import (
"fmt"
)
func main() {
// 在 fmt.Printf 中使用 %t 占位符表示布尔值
fmt.Printf("3 等于 2 吗?%t\n", 3 == 2)
fmt.Printf("2 等于 2 吗?%t\n", 2 == 2)
// 判断 a 和 b 是否相等
a, b := 1, 2
fmt.Printf("a 是 %d,b 是 %d\n", a, b)
if a > b {
fmt.Println("a 大于 b")
} else if a == b {
fmt.Println("a 等于 b")
} else {
fmt.Println("b 大于 a")
}
}
运行程序后,我们得到以下输出:
go run bool.go
3 等于 2 吗?false
2 等于 2 吗?true
a 是 1,b 是 2
b 大于 a
输出解释:
3 等于 2 吗?false
:表达式 3 == 2
的值为 false
,然后使用 %t
占位符打印。2 等于 2 吗?true
:表达式 2 == 2
的值为 true
,然后使用 %t
占位符打印。a 是 1,b 是 2
:这行打印了整数变量 a
和 b
的赋值。b 大于 a
:if-else if-else
条件语句的评估结果为 a < b
,即 1 < 2
(true),因此执行 else
语句,程序打印 "b 大于 a"。在这个程序中,我们首先在 fmt.Printf
中使用 %t
表示布尔值,然后使用 if
语句判断两个值之间的关系。在 fmt.Printf
中,我们可以使用 %d
占位符表示整数,使用 %f
占位符表示浮点数,使用 %t
占位符表示布尔值。
Go 语言还内置了复数类型,可以分为 complex64
和 complex128
两种类型。在 complex64
中,实部和虚部都是 32 位的,而在 complex128
中,实部和虚部都是 64 位的。我们可以在 Go 中轻松地进行复数运算。
创建一个名为 complex.go
的文件,并输入以下代码:
cd ~/project
touch complex.go
package main
import (
"fmt"
)
func main() {
// 以不同方式初始化复数
c1 := complex(3, 1)
c2 := 4 + 5i
// 复数运算
c3 := c1 + c2
c4 := c1 * c2
// 使用 real() 函数获取复数的实部,使用 imag() 函数获取复数的虚部
fmt.Printf("c1 的实部是 %v,虚部是 %v\n", real(c1), imag(c1))
// fmt.Printf 中的 %v 可以用来表示复数
fmt.Printf("c1 + c2 的结果是 %v\n", c3)
fmt.Printf("c1 * c2 的结果是 %v\n", c4)
}
运行程序后,我们得到以下输出:
go run complex.go
c1 的实部是 3,虚部是 1
c1 + c2 的结果是 (7+6i)
c1 * c2 的结果是 (7+19i)
输出解释:
c1 的实部是 3,虚部是 1
:函数 real(c1)
提取复数 c1
的实部,即 3
,而 imag(c1)
提取 c1
的虚部,即 1
。这些值随后使用 %v
占位符打印。c1 + c2 的结果是 (7+6i)
:这行显示了复数 c1
和 c2
相加的结果。c1
定义为 3 + 1i
,c2
定义为 4 + 5i
。分别将实部和虚部相加,得到 (3 + 4) + (1 + 5)i
,即 7 + 6i
。c1 * c2 的结果是 (7+19i)
:这行显示了复数 c1
和 c2
相乘的结果。(3 + 1i) * (4 + 5i)
的计算结果为 (3*4 - 1*5) + (3*5 + 1*4)i
,简化为 (12-5) + (15+4)i
,最终为 7+19i
。在这个程序中,我们演示了如何使用 complex
函数以及 +
和 *
运算符来初始化和进行复数运算。real()
和 imag()
分别用于提取复数的实部和虚部。fmt.Printf
中的 %v
占位符用作通用占位符来显示复数。
在 Go 1.13 版本中,引入了 Numeric Literal Syntax
(数字字面值语法)。它定义了不同进制数字的表示方式。让我们来看看它的规则。
0b
。例如,0b101
等价于十进制的 5
。0o
或 0O
。例如,0o11
等价于十进制的 9
。0x
或 0X
。例如,0x1b
等价于十进制的 27
。_
分隔整数中的数字,例如 0b1000_0100_0010_0001
等价于 0b1000010000100001
。这可以提高可读性。让我们通过代码详细演示:
cd ~/project
touch literals.go
package main
import "fmt"
func main() {
// 二进制,以 0b 开头
var a int = 0b101
fmt.Printf("二进制 a 是 %b,十进制是 %d\n", a, a)
// 八进制,以 0o 或 0O 开头
var b int = 0o11
fmt.Printf("八进制 b 是 %o,十进制是 %d\n", b, b)
// 十六进制,以 0x 或 0X 开头
var c int = 0x1b
fmt.Printf("十六进制 c 是 %x,十进制是 %d\n", c, c)
// 使用分隔符
d := 0b1000_0100_0010_0001
e := 0b1000010000100001
if d == e {
fmt.Println("d 等于 e")
}
}
运行程序后,我们得到以下输出:
go run literals.go
二进制 a 是 101,十进制是 5
八进制 b 是 11,十进制是 9
十六进制 c 是 1b,十进制是 27
d 等于 e
输出解释:
二进制 a 是 101,十进制是 5
:这行显示了 0b101
的二进制表示形式为 101
,其十进制等价值为 5
。%b
占位符显示 a
的二进制值,而 %d
显示其十进制表示。八进制 b 是 11,十进制是 9
:八进制数 0o11
等价于十进制的 1*8^1 + 1*8^0 = 8 + 1 = 9
。%o
占位符显示 b
的八进制表示,%d
显示其十进制值。十六进制 c 是 1b,十进制是 27
:十六进制数 0x1b
等价于十进制的 1*16^1 + 11*16^0 = 16 + 11 = 27
。%x
占位符显示 c
的十六进制表示,%d
显示其十进制值。d 等于 e
:这表明二进制字面值 0b1000_0100_0010_0001
与 0b1000010000100001
的值相等。下划线仅用于提高可读性,不会改变数字的值。if
条件正确评估为 true
。在这个程序中,我们演示了不同进制数字的声明和输出,以及分隔符的使用。在 fmt.Printf
中,我们使用了不同的占位符来表示不同的进制。例如,%b
表示二进制,%x
表示十六进制。掌握这些将提高编程效率。
让我们回顾一下本节学到的内容:
int
和 uint
的范围取决于平台在本节中,我们讲解并演示了常见的整数类型、浮点数类型和布尔类型。我们讨论了它们的大小、范围和使用方法。此外,我们还介绍了复数字面值和字面值常量。数值类型是 Go 程序的基石,学习者需要认真学习它们。