数值类型前言: 由于去年的项目,合作方的服务使用的是Golang开发的,合作期间也进行过简单的探讨。加之时下Go、Rust这些语言大热🔥,最终决定把这门语言系统的学一下。开始以语言最基本特性和语法入手,毕竟都有过其他开发语言的学习经历,也基于Go语言自身的简洁性,学习和上手还都是比较顺畅的。学习的时候喜欢边看边敲做些笔记,前段时间又稍微整理完善了下就水成一份博客发一下吧。
- 📖参考的主要课本为《Go语言趣学指南》(网上目前只找到英文版)、《Go语言实战》与《Go Web编程》,也包含一些网上的博文与教程之类的,;
- 📓每个lesson也就包含一份示例源码和一份笔记,简单且系统地记录下相关知识点和一些自己的理解;
- 🐶因为学习Golang之前,大家很多都是有了其他语言的基础,所以过于基本的语法就无需细究了;
- 源码地址:https://github.com/Xuhy0826/Go-Go-Study
实数
浮点类型
go语言中有两种浮点类型:
- float64(占8字节内存),默认的浮点类型。意思是说不显式地指定float32,都是使用float64来定义一个带小数点的变量
- float32(占4字节内存)
days := 365.2425 //短声明,days会被Go编译器推断为浮点类型默认的float64类型
如果需要使用float32,我们必须指定类型
var pi64 = math.Pi
var pi32 float32 = math.Pi //这样声明的变量才会是float32
零值
零值就是某个类型的默认值。在Go中,当创建一个变量但没有为其赋值时,Go会自动为其赋值对应类型的零值。浮点类型的零值很容易想到
var price float
等同于
price := 0.0
格式化输出浮点值
fmt.Printf()
fmt.Printf("%f\n", third) //0.333333
fmt.Printf("%.2f\n", third) //0.33,.2f就是表示小数点后保留2位
fmt.Printf("%4.2f\n", third) //0.33,4.2f表示总宽(长)度为4,小数点后保留2位
fmt.Printf("%5.2f\n", third) //0.33,5.2f表示总宽(长)度为5,小数点后保留2位,长度不够使用空格来补
fmt.Printf("%05.2f\n", third) //00.33,05.2f表示总宽(长)度为5,小数点后保留2位,长度不够使用“0”来补
浮点类型的精确性
由于计算机只能通过0和1来表示浮点数,所以浮点数会经常受到舍入错误的影响 比如:
piggyBank := 0.1
piggyBank += 0.2
fmt.Println(piggyBank) //0.30000000000000004
由上面提到的浮点类型的精确度问题,就会导致浮点数的比较出现意外。
fmt.Println(piggyBank == 0.3) //false
一个折中的解决方案就是使用一定精确度来判断是否相等
fmt.Println(math.Abs(piggyBank-0.3) < 0.0001) //true
那么说到底,避免浮点数精确度问题的最佳方案就是:不使用浮点数🐶
整数
Go中提供了10种整数类型,根据不同的大小和是否有符号分为
- int8 1字节(8位)
- uint8 1字节
- int16 1字节
- uint16 1字节
- int32 1字节
- uint32 1字节
- int64 1字节
- uint64 1字节
- 还有两个是int和uint,Go在进行类型推断是会默认推断成int类型。在Go中,int类型会根据目标硬件(电脑是32位还是64位)选择合适的位长(32位机器上int就是32位值,64位机器就是64位长),所以如果想在32位的机器上操作特别大的数,要定义成int64而不是int。但是也不要认为int类型和int32或int64是一种类型,他们是3种类型。
选择合适的类型
适应不同的常见选择不同的类型来 【例】:使用uint8来表示颜色rgb值,是个很好的选择:
- (1)能将变量限制在合法的范围之内
- (2)对于未压缩的图片这种需要按顺序存储大量颜色的场景,可以极大的节省空间
var red, green, blue uint8 = 0, 141, 213
fmt.Printf("color:#%02x%02x%02x;", red, green, blue) //color:#008dd5;
回绕(wrap around)
整数类型不会有浮点类型的精度问题,但是存在自己的回绕问题。就是整数类型在达到自己的边界值(最大值或最小值),再向边界外延伸时,会回到最小值或最大值。例如一个uint8,它的最大值应该是255,此时再进行加1操作后,值就变成了0。其实只要知道整型在计算机中的二进制存储方式,这个事情还是很好理解的。
var numberA uint8 = 255 //到达类型最大值
numberA++
fmt.Println(numberA) //0 环绕
大数
顾名思义,就是特别大的数,一般情况下,比较大的数我们也可使用简便写法 表示方法(类似科学计数法):
var distance int64 = 41.3e12 //就是41.3 * 10<sup>12</sup>
但是如果需要使用超过uint64上限的数时,Go为我们提供了big包来解决问题,引用时包名为:"math/big"。
big包
- 存储大整数:big.Int
- 存储任意精度的浮点数:big.Float
- 存储如1/3的分数:big.Rat
big.Int的创建方式有两种
- 使用big.NewInt(int val)的方法
lightSpeed := big.NewInt(299792)
distance := new(big.Int)
distance.SetString("24000000000000000000000", 10)
大数类型可以精确地承载很大的数值,但是代价就是空间和性能的损耗。
大数在常量中的表现
和变量不同,当我们不为常量指定类型,并直接为其赋值一个很大的数,Go会直接将其标记为无类型(untyped)而不会引发溢出异常,并且可以在程序中正常使用
const distance = 240000000000000000000000
fmt.Println("Andromeda Galaxy is ", distance/299792/86400) //output: Andromeda Galaxy is 9265683466462
字符串类型
string类型
关于字符串类型(string),和其他语言一样没什么区别,使用双引号包起来,如
peace := "peace"
var peace = "peace"
var peace string = "peace"
\n
fmt.Println(`
peace be upon you
upon you be peace`)
“字符串字面量”和“原始字符串字面量”都是string类型。
字符、代码点、符文和字节
- 都知道计算机中字符是通过编码存取的,也就是每个字符都使用一个特定的数字表示。比如A就是65,那么书中将这个65称为字符A的代码点。
- rune类型(符文类型):Go中使用rune类型来表示字符的代码点,该类型本质上是int32类型的别名,也就是说rune和int32可以相互转换
- byte类型:Go中的byte类型不仅可以表示二进制数据,而且被拿来表示ASCII码(ASCII共包含128个字符)。本质上byte类型是uint8类型的别名
var pi rune = 960
var alpha rune = 940
var omega rune = 969
var bang byte = 33
fmt.Printf("%v %v %v %v\n", pi, alpha, omega, bang) //960 940 969 33
//通过使用格式化变量%c,可以将代码点表示成字符
fmt.Printf("%c %c %c %c\n", pi, alpha, omega, bang) //π ά ω !
在Go中使用单引号来表示字符字面量,如果用户声明一个字符变量而没有为其制定类型,那么Go会将其推断成rune类型。下面三种写法是一样的功能。
grade := 'A'
var grade = 'A'
var grade rune = 'A'
字符串无法修改
字符串虽然是有字符“串”起来的,但是和C#、java等语言一样,Go中的字符串类型也是不可修改的。
//通过索引的方式访问字符串中的字符
message := "shalom"
c := message[5]
fmt.Printf("%c\n", c)
//字符串不可被修改
//message[5] = 'd' //报错:cannot assign to message[5]
字符串与符文
Golang中字符串使用utf-8编码。utf-8是一种变长的编码方式,也就是说每个字符的长度可能占用不同的字节长度。比如中文字符就是需要占据两个字节长度,而英文字符或者数字则只需要占据1个字节长度。 为了方便,Go为此提供了utf包,里面提供了两个实用的方法
- RuneCountInString函数,能够按照特定的字符返回字符串中字符的个数,而不是像len方法一样返回字节的长度
- DecodeRuneInString函数,解码字符串的首个字符并返回解码后的字符占用的字节长度
question := "今天星期几?"
fmt.Println(len(question), "bytes") //18 bytes
fmt.Println(utf8.RuneCountInString(question), "runes") //6 runes
c, size := utf8.DecodeRuneInString(question)
fmt.Printf("First rune: %c %v bytes", c, size) //First rune: 今 3 bytes
//遍历字符串,挨个打印出来
for i, c := range question {
fmt.Printf("%v %c\n", i, c)
}
rangeic
Go中与其他强类型语言(比如C#)类似,类型之间进行操作时,需要经过类型转换否则会报“类型不匹配”的错误。
数字类型转换
- 整数类型 → 浮点类型:
age := 41
marsAge := float64(age)
- 浮点类型 → 整数类型:
【注意】:浮点型的小数部分是被截断,而不是四舍五入
earthDays := 365.2425
fmt.Println(int(earthDays)) //output: 365
在数值类型进行转换时,一样要注意超出范围的问题,比如一个较大float64转成int16时。
字符串转换
- rune/byte → string
var pi rune = 960
var alpha rune = 940
fmt.Println(string(pi), string(alpha)) //output: π ά
- 数字类型 → string
情况特殊一点,为了将一串数组转换为string类型,必须将其中的每个数字都转换为相应的代码点(char)。也就是代表字符0的48~代表字符9的57。我们需要使用到strconv(代表“string conversion”)包提供的Itoa函数来完成这一工作。
countdown := 10
str := "Launch in T minus " + strconv.Itoa(countdown) + " seconds".
另一种方法,使用fmt.Sprintf函数,该函数会返回格式化后的string而不是打印
countdown := 9
str := fmt.Sprintf("Launch in T minus %v seconds", countdown)
fmt.Println(str) //Launch in T minus 9 seconds
- string → 数字 一种不太常用的转换,也是使用strconv包的Atoi函数
count, err := strconv.Atoi("10")
if err != nil {
//出错
}
fmt.Println(count) //10
上面这种写法是之后经常看到的,是Go处理异常的一种常用写法。由于Go的函数可以返回多个值,一般会将可能产生的异常一并返回。
布尔类型转换
fmtPrint
launch := false
launchText := fmt.Sprintf("%v", launch)
fmt.Println("Ready for launch:", launchText) //Ready for launch: false
某些语言中会把1和0当做true和false,Go中是不行的。
这里给大家推荐一个在线软件复杂项交易平台:米鼠网 https://www.misuland.com
米鼠网自成立以来一直专注于从事软件项目、人才招聘、软件商城等,始终秉承“专业的服务,易用的产品”的经营理念,以“提供高品质的服务、满足客户的需求、携手共创双赢”为企业目标,为中国境内企业提供国际化、专业化、个性化、的软件项目解决方案,我司拥有一流的项目经理团队,具备过硬的软件项目设计和实施能力,为全国不同行业客户提供优质的产品和服务,得到了客户的广泛赞誉。