Golang 中的数值类型

GolangGolangBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

介绍

欢迎 Gophers 来到这个新章节。在本节中,我们将学习数值类型。内容包括常用的整数类型、浮点数类型、布尔类型,以及复数类型和 Go 1.13 版本中引入的字面值语法。

知识点:

  • 整数类型
  • 浮点数类型
  • 布尔类型
  • 复数类型
  • 字面值语法

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL go(("`Golang`")) -.-> go/BasicsGroup(["`Basics`"]) go(("`Golang`")) -.-> go/FunctionsandControlFlowGroup(["`Functions and Control Flow`"]) go(("`Golang`")) -.-> go/AdvancedTopicsGroup(["`Advanced Topics`"]) go/BasicsGroup -.-> go/values("`Values`") go/BasicsGroup -.-> go/variables("`Variables`") go/FunctionsandControlFlowGroup -.-> go/if_else("`If Else`") go/AdvancedTopicsGroup -.-> go/number_parsing("`Number Parsing`") go/AdvancedTopicsGroup -.-> go/base64_encoding("`base64 Encoding`") subgraph Lab Skills go/values -.-> lab-149067{{"`Golang 中的数值类型`"}} go/variables -.-> lab-149067{{"`Golang 中的数值类型`"}} go/if_else -.-> lab-149067{{"`Golang 中的数值类型`"}} go/number_parsing -.-> lab-149067{{"`Golang 中的数值类型`"}} go/base64_encoding -.-> lab-149067{{"`Golang 中的数值类型`"}} end

整数类型

整数可以大致分为两类:无符号整数(unsigned integers)和有符号整数(signed integers)。其中,有符号整数是最广泛使用的。

无符号整数只能表示非负数(0 和正数),而有符号整数可以表示负数和非负数。

无符号整数可以分为四种大小:8 位、16 位、32 位和 64 位,分别用 uint8uint16uint32uint64 表示。对应的有符号整数为 int8int16int32int64。下表展示了每种类型表示的不同范围:

类型 描述 范围
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

uint8int8 为例,它们都是 8 位整数,可以表示 256 个值。在无符号整数类型 uint8 中,其表示的范围是 0 到 255,而在有符号整数类型 int8 中,其表示的范围是 -128 到 127。

除了上述 8 种类型外,还有三种特殊的整数类型:uintintuintptr。其中,uintint 在不同平台上可能表示不同的范围,而 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 字节),表明它是 int64unsafe.Sizeof(a) 返回变量 a 占用的字节数,乘以 8 转换为位。这意味着 int 类型可以存储更大的整数值。
  • b 的值为 125,类型为 int8:这里我们声明了一个 int8 类型的变量 b 并赋值为 125。输出确认了这一点,显示了值和数据类型。
  • c + d = 510 - 5 = 58 * 10 = 80:这些行展示了基本的整数算术运算:加法、减法和乘法。输出确认了这些计算的结果。

在这个文件中,unsafe.Sizeof() 函数可以用于获取当前变量类型占用的字节数。1 字节(byte)等于 8 位,因此 unsafe.Sizeof()*8 可以获取该类型占用的位数。从输出中可以看出,在线环境是 64 位的。在线环境中 int 的实际类型是 int64

我们可以在终端中使用以下命令来确定当前系统的架构:

dpkg --print-architecture
amd64

浮点数

浮点数(Floating-point numbers),也称为小数,在 Go 中可以使用两种浮点类型表示:float32float64。默认的浮点类型是 float64

float32float64 表示不同的精度。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 类型只有两个可能的值:truefalse,其中 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:这行打印了整数变量 ab 的赋值。
  • b 大于 aif-else if-else 条件语句的评估结果为 a < b,即 1 < 2(true),因此执行 else 语句,程序打印 "b 大于 a"。

在这个程序中,我们首先在 fmt.Printf 中使用 %t 表示布尔值,然后使用 if 语句判断两个值之间的关系。在 fmt.Printf 中,我们可以使用 %d 占位符表示整数,使用 %f 占位符表示浮点数,使用 %t 占位符表示布尔值。

复数类型

Go 语言还内置了复数类型,可以分为 complex64complex128 两种类型。在 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):这行显示了复数 c1c2 相加的结果。c1 定义为 3 + 1ic2 定义为 4 + 5i。分别将实部和虚部相加,得到 (3 + 4) + (1 + 5)i,即 7 + 6i
  • c1 * c2 的结果是 (7+19i):这行显示了复数 c1c2 相乘的结果。(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
  • 八进制:在整数前添加 0o0O。例如,0o11 等价于十进制的 9
  • 十六进制:在整数前添加 0x0X。例如,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_00010b1000010000100001 的值相等。下划线仅用于提高可读性,不会改变数字的值。if 条件正确评估为 true

在这个程序中,我们演示了不同进制数字的声明和输出,以及分隔符的使用。在 fmt.Printf 中,我们使用了不同的占位符来表示不同的进制。例如,%b 表示二进制,%x 表示十六进制。掌握这些将提高编程效率。

总结

让我们回顾一下本节学到的内容:

  • 整数可以分为有符号整数和无符号整数
  • 默认的整数类型 intuint 的范围取决于平台
  • 浮点数的表示方式
  • 如何使用复数
  • 字面值语法的介绍
  • 如何使用不同的占位符

在本节中,我们讲解并演示了常见的整数类型、浮点数类型和布尔类型。我们讨论了它们的大小、范围和使用方法。此外,我们还介绍了复数字面值和字面值常量。数值类型是 Go 程序的基石,学习者需要认真学习它们。

您可能感兴趣的其他 Golang 教程