Go 语言中的数值类型

58 阅读7分钟

Go 语言中的数值类型

Go 语言原生支持的数值类型包括整型、浮点型和复数类型,它们适用于不同的场景。

整型

Go 语言的整型,主要用来表示现实世界中整型数量;比如:人的年龄、公司人数等等

Go 语言中分为平台无关整型和平台相关整型两种,它们的区别主要是 CPU 架构或操作系统的字节长度,在 32 位的系统上,int 类型是 32 位的(-231 到 231 - 1);在 64 位的系统上 ,int 类型是 64 位的(-263 到-263-1);如果 int64 在 32 位的系统上,它的极限值也只能是 int32 的取值范围。

平台无关整型和平台相关整型

有符号整型(int8 ~ int64)和无符号整型(uint8 ~uint64)。两者的本质差别在于最高二进制位(bit 位)是否被解释为符号位,这点会影响到无符号整型与有符号整型的取值范围。

  • 平台无关整型又分有符号整型和和无符号整型。

    有符号整型表示的数值可以为负数、零和正数;比如:int8int16int32int64

    无符号整型表示的数值只能为零和正数;比如:uint8uint16uint32uint64

  • 平台相关型整数

    intuintuintptrbyte其实等价与 uint8 类型,可以理解为 uint8 类型的别名,用于定义一个字节,所以字节类型也属于整型

整型的溢出问题

无论哪种整型,都有它的取值范围,也就是有它可以表示的值边界。如果这个整型因为参与某个运算,导致结果超出了这个整型的值边界,我们就说发生了整型溢出的问题。

package main

import "fmt"

func main() {
	var s int8 = 127
	s += 1
	fmt.Println(s) // 预期128,实际结果-128

	var u uint8 = 1
	u -= 2
	fmt.Println(u) // 预期-1,实际结果255
}

这个问题最容易发生在循环语句的结束时的条件判断中;无论无符号整型,还是有符号整型都存在溢出的问题,所以我们要十分小心地选择参与循环语句结束判断的整型变量类型,以及与之比较的边界值。

字面值与格式化

早期 Go 版本支持十进制、八进制、十六进制的数值字面值形式:

a := 53        // 十进制
b := 0700      // 八进制,以"0"为前缀
c := 0xaabbcc  // 十六进制,以"0x"为前缀
d := 0Xddeeff  // 十六进制,以"0X"为前缀

Go 1.13 版本中,Go 又增加了对二进制字面值的支持和两种八进制字面值的形式;

a := 0b10000001 // 二进制,以"0b"为前缀
b := 0B10000001 // 二进制,以"0B"为前缀
c := 0o700      // 八进制,以"0o"为前缀
d := 0O700      // 八进制,以"0O"为前缀

当然,也支持使用 _ 来分割数字

package main

import "fmt"

func main() {
    bignumber := 42_433_432
    fmt.Println("bignumber:", bignumber) // bignumber: 42433432
}

为提升字面值的可读性,Go 1.13 版本还支持在字面值中增加数字分隔符“_”,分隔符可以用来将数字分组以提高可读性。

Go 1.13 中增加的二进制字面值以及数字分隔符,只在 go.mod 中的 go version 指示字段为 Go 1.13 以及以后版本的时候,才会生效,否则编译器会报错。

通过标准库 fmt 包的格式化输出函数将一个整型变量输出位不同进制的形式

package main

import "fmt"

func main() {
	var a int8 = 59
	fmt.Printf("%b\n", a) //输出二进制:111011
	fmt.Printf("%d\n", a) //输出十进制:59
	fmt.Printf("%o\n", a) //输出八进制:73
	fmt.Printf("%O\n", a) //输出八进制(带0o前缀):0o73
	fmt.Printf("%x\n", a) //输出十六进制(小写):3b
	fmt.Printf("%X\n", a) //输出十六进制(大写):3B
}

常用类型转换方法

  • 整数转浮点数

    package main
    
    func main(){
        var a int = 10
        // 字面量转换方式
        fmt.Printf("%f\n", float64(a)) // 10.000000
        
        // 使用 strconv 包的 ParseFloat 函数转换
        var b float64
        b, _ = strconv.ParseFloat(strconv.Itoa(a), 64)
        fmt.Printf("%f\n", b) // 10.000000
        
        // 使用字符串格式化函数 fmt.Sprintf 将整数格式化为带有小数点的字符串,然后使用 strconv 包中的 ParseFloat 函数将字符串转换为浮点数
        formattedString := fmt.Sprintf("%.1f", float64(a))
        b, _ = strconv.ParseFloat(formattedString, 64)
        fmt.Println("b TypeOf:", reflect.TypeOf(b)) // b TypeOf: float64
    }
    
  • 整数转字符串

    package main
    
    import "fmt"
    
    func main(){
       	var a int = 10
    	str := strconv.Itoa(a)
    	fmt.Printf("str: %s, type of: %s\n", str, reflect.TypeOf(str)) // str: 10, type of: string
    
    	str = fmt.Sprintf("%d", a)
    	fmt.Printf("str: %s, type of: %s\n", str, reflect.TypeOf(str)) // str: 10, type of: string
    
    	str = strconv.FormatInt(int64(a), 10)
    	fmt.Printf("str: %s, type of: %s\n", str, reflect.TypeOf(str)) // str: 10, type of: string
    }
    
  • 整数转布尔

    关系运算符、相等或不相等

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var a int = 10
    	fmt.Printf("a: %v\n", a > 10)  // a: false
    	fmt.Printf("a: %v\n", a < 10)  // a: false
    	fmt.Printf("a: %v\n", a >= 10) // a: true
    	fmt.Printf("a: %v\n", a <= 10) // a: true
    	fmt.Printf("a: %v\n", a == 10) // a: true
    	fmt.Printf("a: %v\n", a != 10) // a: false
    }
    

浮点型

浮点数代表现实中的小数;Go 语言中提供了两种精度的浮点数,分别是float32float64;无论是 float32 还是float64,他们的变量的默认值都是 0.0

字面值与格式化输出

  • fmt 包也提供了针对浮点数的格式化输出。我们最常使用的格式化输出形式是 %f。通过 %f,我们可以输出浮点数最直观的原值形式:

    var f float64 = 123.45678
    fmt.Printf("%f\n", f) // 123.456780
    
  • %e 输出的是十进制的科学计数法形式,而 %x 输出的则是十六进制的科学计数法形式:

    var f float64 = 123.45678
    fmt.Printf("%e\n", f) // 1.234568e+02
    fmt.Printf("%x\n", f) // 0x1.edd3be22e5de1p+06
    

常用方法

  • 浮点数转字符串

    package main
    
    import (
    	"fmt"
    	"reflect"
    	"strconv"
    )
    
    func main() {
    	var a float64 = 10.0
    
    	strNum := strconv.FormatFloat(a, 'f', -1, 64)
    	fmt.Printf("strNum: %s, type of: %s\n", strNum, reflect.TypeOf(strNum)) // strNum: 10, type of: string
    }
    
  • 浮点数转整数

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    func main() {
    	var f float64 = 3.14
    	// 使用类型转换将浮点数转换为整数;通过将浮点数直接赋值给整数类型的变量,Go语言会自动进行类型转换,将浮点数的小数部分截断,得到整数部分。请注意,这种类型转换会丢失浮点数的小数部分
    	b := int(f)
    
    	fmt.Printf("str Num: %d, type of: %s\n", b, reflect.TypeOf(b)) // str Num: 3, type of: int
    
    	// 使用 math 包的函数将浮点数转换为整数
    	i := int(math.Round(f))
    	fmt.Printf("i: %d, type of: %s\n", i, reflect.TypeOf(i)) // i: 3, type of: int
    }
    
  • 浮点数转布尔

    相等运算符、相等、不相等

    package main
    
    import (
    	"fmt"
    )
    
    // ? 待分析
    func main() {
    	var f1 float32 = 16777216.0
    	var f2 float32 = 16777217.0
    	fmt.Println(f1 == f2) // true
    }
    

复数类型

数学课本上将形如 z=a+bi(a、b 均为实数,a 称为实部,b 称为虚部)的数称为复数。

复数字面值的三种表示方法

  • 字面值初始化

    package main
    
    import "fmt"
    
    func main() {
    	var c = 5 + 6i
    	var d = 0o123 + .12345e+5i
    	fmt.Println("c:", c) // 5 + 6i
    	fmt.Println("d:", d) // 83+12345i
    }
    
  • 使用 complex

    package main
    
    import "fmt"
    
    func main() {
    	var c = complex(5, 6)
    	var d = complex(0o123, .12345e+5)
    	fmt.Println("c:", c) // 5 + 6i
    	fmt.Println("d:", d) // 83+12345i
    }
    
  • 使用 realimag 获取一复数的实部与虚部,返回一个浮点类型

    package main
    
    import "fmt"
    
    func main() {
    	var c = complex(5, 6) // 5 + 6i
    	r := real(c)          // 5.000000
    	i := imag(c)          // 6.000000
    	fmt.Println("c:", c)  // c: 5 + 6i
    	fmt.Println("d:", r)  // d: 5
    	fmt.Println("i:", i)  // i: 6
    }
    

自定义数值类型(类型别名)

  • 通过 type 关键字基于原生数值类型来声明一个新类型

    type MyInt int32
    
  • MyInt 与 int32 的值无法直接让他们相互赋值,或者是把他们放在同一个运算中直接计算,否则编译器就会报错;解决方案———显示转换