go语言学习 了解 Go原生支持的数值类型

go语言支持的数值类型

Go 语言原生支持的数值类型包括整型、浮点型以及复数类型

整型

go语言中分为 平台无关整型 和平台有关整型 ,它们的区别主要就在,这些整数类型在不同 CPU 架构或操作系统下面,它们的长度是否是一致的。

平台无关整型

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

平台无关整型,它们在任何 CPU 架构或任何操作系统下面,长度都是固定不变的

有符号整型 int8 int16 init32 int64 无符号整型 uint8 uint16 uint32 uint64 ,他们对应的长度分别为 1,2,3,4 个字节长度 ,每种类型 对应的取值范围也是不同的

这是因为 Go 采用 2 的补码(Two’s Complement)作为整型的比特位编码方法。因此,我们不能简单地将最高比特位看成负号,把其余比特位表示的值看成负号后面的数值。Go 的补码是通过原码逐位取反后再加 1 得到的

举栗子说明 我们以 -127 这个值为例,它的补码转换过程就是这样的

127 的源码是 01111111
源码取反 10000000 (源码的基础上0变成1,1变成0)
取反的结果+1 10000001

本人目前也是小白,目前知道这个概念就可以了,主要是了解并学会对应类型的编码使用方面,后面一些概念性问题会深入学习下,再去写博客记录

平台相关整型

它们是 int 默认的有符号整型,uint 默认的无符号整型,与 uintptr 无符号整型
int 和 uint 对应的是32 长度 32位 四字节长度 ,64位8字节长度,
uintptr 长度定义是 大大可以存储任意一个指针的值

这三个类型的长度是平台相关的,所以我们在编写有移植性要求的代码时,千万不要强依赖这些类型的长度。如果你不知道这三个类型在目标运行平台上的长度,可以通过 unsafe 包提供的 SizeOf 函数来获取


var a, b = int(5), uint(6)
var p uintptr = 0x12345678
fmt.Println("signed integer a's length is", unsafe.Sizeof(a)) // 8
fmt.Println("unsigned integer b's length is", unsafe.Sizeof(b)) // 8
fmt.Println("uintptr's length is", unsafe.Sizeof(p)) // 8

go 整型溢出问题

每一个整型都有他的取值范围,会存在溢出问题,当整型类型变量参与运算的时候需要 关注下是否会存在溢出问题


var s int8 = 127
s += 1 // 预期128,实际结果-128

var u uint8 = 1
u -= 2 // 预期-1,实际结果255

字面值与格式化输出

字面值是指在程序中无需变量保存,可直接表示为一个具体的数字或字符串的值。比如
在a = b * 2这个语句中,2就是一个字面值,它本身就是一个具体的值

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

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

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


d1 := 0b10000001 // 二进制,以"0b"为前缀
d2 := 0B10000001 // 二进制,以"0B"为前缀
e1 := 0o700      // 八进制,以"0o"为前缀
e2 := 0O700      // 八进制,以"0O"为前缀
//Go 1.13 版本之后字面值增加了分隔符 ,为了更好查看字面值格式
a := 5_3_7 //537

格式化输出

fmt 包的格式化输出函数,将一个整型变量输出为不同进制的形式


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

浮点型

go 和 大部分主流语言 都是用了 IEEE 754 是 IEEE 制定的二进制浮点数算术标准,

IEEE 754 标准规定了四种表示浮点数值的方式:单精度(32 位)、双精度(64 位)、扩展单精度(43 比特以上)与扩展双精度(79 比特以上,通常以 80 位实现)。后两种其实很少使用,我们重点关注前面两个

对比go 语言来说 float32 和 float64 对应 IEEE 754 中得单精度 和 双精度类型

无论是 float32 还是 float64,它们的变量的默认值都为 0.0,不同的是它们占用的内存空间大小是不一样的,可以表示的浮点数的范围与精度也不同

浮点数在内存中的二进制表示分三个部分:符号位、阶码(即经过换算的指数),以及尾数

单精度(float32)与双精度(float64)浮点数在阶码和尾数上的不同。这两种浮点数的阶码与尾数所使用的位数是不一样的,你可以看下 IEEE 754 标准中单精度和双精度浮点数的各个部分的长度规定

| 浮点类型 | 符号位(bit位数) | 阶码(bit位数) | 阶码偏移值|尾数(bit位数)|
| 单精度(float32) | 1 | 8 | 127 | 23 |
| 双精度(float64) | 1 | 11 | 1023 | 52 |

一个十进制形式的浮点值 139.8125,转换为 IEEE 754 规定中的那种单精度二进制表示

步骤一:我们要把这个浮点数值的整数部分和小数 部分,分别转换为二进制形式(后缀 d 表示十进制数,后缀 b 表示二进制数):

整数部分:139d => 10001011b;
小数部分:0.8125d => 0.1101b(十进制小数转换为二进制可采用“乘 2 取整”的竖式计算)。

乘 2 取整 0.8125乘2 = 1.625 1 0.625乘2 = 1.25 1 0.25乘2 = 0.5 0 0.5乘2 = 1 1 ,我们看到乘2 进到值为 整数时不在进行计算,总共进行4次计算,每次计算的取对应的整数值 求得二进制值 为 0.1101

步骤二:移动小数点,直到整数部分仅有一个 1
也就是 10001011.1101b => 1.00010111101b。我们看到,为了整数部分仅保留一个 1,小数点向左移了 7 位,这样指数就为 7,尾数为 00010111101b。

步骤三:计算阶码。
IEEE754 规定不能将小数点移动得到的指数,直接填到阶码部分,指数到阶码还需要一个转换过程。对于 float32 的单精度浮点数而言,阶码 = 指数 + 偏移值。偏移值的计算公式为 2^(e-1)-1,其中 e 为阶码部分的 bit 位数,这里为 8,于是单精度浮点数的阶码偏移值就为 2^(8-1)-1 = 127。这样在这个例子中,阶码 = 7 + 127 = 134d = 10000110b。float64 的双精度浮点数的阶码计算也是这样的。

步骤四:将符号位、阶码和尾数填到各自位置,得到最终浮点数的二进制表示。尾数位数不足 23 位,可在后面补 0。

二进制标志前缀字面值 0b
符号位为 0
整数位置为 10000110
尾数位置 为 00010111101 不足23 位后面 补0 00010111101_000000000000
这样,最终浮点数 139.8125d 的二进制表示就为 0b_0_10000110_00010111101_000000000000。

字面值与格式化输出

字面值


3.1415
.15  // 整数部分如果为0,整数部分可以省略不写
81.80
82. // 小数部分如果为0,小数点后的0可以省略不写

格式化输出


var f float64 = 123.45678
fmt.Printf("%f\n", f) // 123.456780

复数型

数学课本上将形如 z=a+bi(a、b 均为实数,a 称为实部,b 称为虚部)的数称为复数,这里我们也可以这么理解

Go 提供两种复数类型,它们分别是 complex64 和 complex128,complex64 的实部与虚部都是 float32 类型,而 complex128 的实部与虚部都是 float64 类型。如果一个复数没有显示赋予类型,那么它的默认类型为 complex128

第一种,我们可以通过复数字面值直接初始化一个复数类型变量:

var c = 5 + 6i
fmt.Printf("%f\n", c) // (5.000000+6.000000i)

第二种,Go 还提供了 complex 函数,方便我们创建一个 complex128 类型值:

var c = complex(5, 6) // 5 + 6i
fmt.Printf("%f\n", c) // (5.000000+6.000000i)

第三种,你还可以通过 Go 提供的预定义的函数 real 和 imag,来获取一个复数的实部与虚部,返回值为一个浮点类型

	var c = complex(5, 6)
	r := real(c)
	i := imag(c)

	fmt.Printf("%f\n", c)// (5.000000+6.000000i)
	fmt.Printf("%f\n", r)//5.000000
	fmt.Printf("%f\n", i)//6.000000

go支持的创建自定义的数值类型

Go 提供的类型定义语法,来创建自定义的数值类型,我们可以通过 type 关键字基于原生数值类型来声明一个新类型

type myInt int32

创建自定义的数据类型,和其他类型变量使用的时候 需要显示转换才可以使用

	type myInt int32
	var a int = 1
	var b int32 = 2
	
    var c myInt = a //error
	var d myInt = b //error
	
	var c myInt = myInt(a) //success
	var d myInt = myInt(b) //success

	fmt.Println(c, d)

虽然 使用type 关键词定义了 myInt 类型 是 int32 数据类型数据,对于go语言来说 他是创建了一个新的类型,不等价与int32 数值类型的值, 在赋值的使用需要显示转换才可以使用

Go 提供的类型别名(Type Alias)语法来自定义数值类型。
和上面使用标准 type 语法的定义不同的是,通过类型别名语法定义的新类型与原类型别无二致,可以完全相互替代

	type myInt = int32
	var a int32 = 1

	var c myInt = a  // success
	fmt.Println(c) // 1

还有老师留下的思考题

分析下面例子中 f1 为何会与 f2 相等?


var f1 float32 = 16777216.0
var f2 float32 = 16777217.0
f1 == f2 // true

解析问题过程其实和老师讲的 一个十进制形式的浮点值 怎么转换成二进制的过程有关系

f1 变量值是 16777216.0 转换成 二进制 只有整数位 ,不用考虑小数位小数位为0值

十进制转二进制

16777216d => 1000000000000000000000000b

步骤二:移动小数点,直到整数部分仅有一个 1

1000000000000000000000000 => 1.000000000000000000000000
尾数为 000000000000000000000000b ,24位数 指数为 24
步骤三:计算阶码
阶码 = 指数 + 偏移值。偏移值的计算公式为 2^(e-1)-1,其中 e 为阶码部分的 bit 位数,这里为 8,于是单精度浮点数的阶码偏移值就为 2^(8-1)-1 = 127。这样在这个例子中,阶码 = 24 + 127 = 151d = 10010111b

步骤四:尾数保留23位,单精度最大比特位 为23位 不够用0补全
1.000000000000000000000000 尾数已经是24位 只保留23位 要去掉一个0

最终的二进制数字为
0b_0_10010111_00000000000_000000000000

	var f float32 = 16777216.0
	bits := math.Float32bits(f)
	fmt.Printf("%b\n", bits)//输出的二进制 数字内容 1001011100000000000000000000000

f2 变量值是 16777217.0 转换成 二进制 只有整数位 ,不用考虑小数位小数位为0值

十进制转二进制

16777217d => 1000000000000000000000001b

步骤二:移动小数点,直到整数部分仅有一个 1

1000000000000000000000000 => 1.000000000000000000000001
尾数为 000000000000000000000001b ,24位数 指数为 24
步骤三:计算阶码
阶码 = 指数 + 偏移值。偏移值的计算公式为 2^(e-1)-1,其中 e 为阶码部分的 bit 位数,这里为 8,于是单精度浮点数的阶码偏移值就为 2^(8-1)-1 = 127。这样在这个例子中,阶码 = 24 + 127 = 151d = 10010111b

步骤四:尾数保留23位,单精度最大比特位 为23位 不够用0补全
1.000000000000000000000000 尾数已经是24位 只保留23位 要去掉一个0

我们可以看到 16777216.0 和 16777217.0 的尾数都是 24位,对于单精度 float32 的尾数限制最大单位 为 23 位,会保留前23位的尾数,前23位都是一样的 所以 f1 == f2 // true 是成立的

好记性不如烂笔头 本文学自 极客时间 Tony Bai · Go 语言第一课

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值