Appearance
浮点型与复数类型
浮点型
浮点型也叫浮点数,用于表示包含小数点的数据,比如 3.14
、1.00
都是浮点型数据。
其他类型 | ||||
---|---|---|---|---|
类型 | 字节长度 | 默认值 | 备注 | |
float32 | 4 | 0.0 | 浮点型,所有IEEE-754 32位浮点数的集合,12位有效数字 | |
float64 | 8 | 0.0 | 浮点型,所有IEEE-754 64位浮点数的集合,16位有效数字 |
浮点数的表示
Go 语言中的浮点数采用IEEE-754 标准的表达方式,定义了两个类型:float32
和 float64
,其中 float32
是单精度浮点数,可以精确到小数点后 7 位(类似 PHP、Java 等语言的 float
类型),float64
是双精度浮点数,可以精确到小数点后 15 位(类似 PHP、Java 等语言的 double
类型)。
在 Go 语言里,定义一个浮点型变量的代码如下:
Go
var floatValue1 float32
floatValue1 = 10
floatValue2 := 10.0 // 如果不加小数点,floatValue2 会被推导为整型而不是浮点型
floatValue3 := 1.1E-10
对于浮点类型需要被自动推导的变量,其类型将被自动设置为 float64
,而不管赋值给它的数字是否是用 32 位长度表示的。因此,对于以上的例子,下面的赋值将导致编译错误:
Go
floatValue1 = floatValue2 // floatValue2 是 float64 类型
编译错误信息如下:
cannot use floatValue2 (type float64) as type float32 in assignment
必须使用这样的强制类型转换才可以:
GO
floatValue1 = float32(floatValue2)
在实际开发中,应该尽可能地使用 float64
类型,因为 math 包中所有有关数学运算的函数都会要求接收这个类型。
浮点数的精度
浮点数不是一种精确的表达方式,因为二进制无法精确表示所有十进制小数,比如 0.1
、0.7
这种,下面我们通过一个示例来给大家直观演示下:
Go
floatValue4 := 0.1
floatValue5 := 0.7
floatValue6 := floatValue4 + floatValue5
注:浮点数的运算和整型一样,也要保证操作数的类型一致,
float32
和float64
类型数据不能混合运算,需要手动进行强制转化才可以,这一点和动态语言不同。
你觉得上面计算结果 floatValue6
的值是多少?0.8?不,它的结果是 0.7999999999999999
,这是因为计算机底层将十进制的 0.1
和 0.7
转化为二进制表示时,会丢失精度,所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。
浮点数的比较
浮点数支持通过算术运算符进行四则运算,也支持通过比较运算符进行比较(前提是运算符两边的操作数类型一致),但是涉及到相等的比较除外,因为我们上面提到,看起来相等的两个十进制浮点数,在底层转化为二进制时会丢失精度,因此不能被表象蒙蔽。
如果一定要判断相等,下面是一种替代的解决方案:
Go
p := 0.00001
// 判断 floatValue1 与 floatValue2 是否相等
if math.Dim(float64(floatValue1), floatValue2) < p {
fmt.Println("floatValue1 和 floatValue2 相等")
}
可以看到,我们的解决方案是一种近似判断,通过一个可以接受的最小误差值 p
,约定如果两个浮点数的差值在此精度的误差范围之内,则判定这两个浮点数相等。这个解决方案也是其他语言判断浮点数相等所采用的通用方案。
注意⚠️:
- 有浮点数参加的运算结果为浮点数
- 一个整数数值可以赋值给浮点类型但是一个整型变量不可以赋值给浮点类型
float32
类型的数据和float64
类型的数据不能相互运算,需要进行类型转换- 建议使用
float64
类型,float32
容易出现误差 - 整型与浮点型无法一起参与运算
Go
var ia, ib int = 3, 2
var fa, fb float64 = 3, 2
var ic int = 3
var fc float64 = 2
fmt.Printf("ia / ib = %v,结果类型为: %T\n", ia/ib, ia/ib)
fmt.Printf("fa / fb = %v,结果类型为: %T\n", fa/fb, fa/fb)
fmt.Printf("ic / fc = %v,结果类型为: %T\n", ic/fc, ic/fc)
/*
ia / ib = 1,结果类型为: int
fa / fb = 1.5,结果类型为: float64
ic/fc 编译
*
复数类型
除了整型和浮点型之外,Go 语言还支持复数类型,与复数相对,我们可以把整型和浮点型这种日常比较常见的数字称为实数,复数是实数的延伸,可以通过两个实数(在计算机中用浮点数表示)构成,一个表示实部(real),一个表示虚部(imag),常见的表达形式如下:
Go
z = a + bi
其中 a、b 均为实数,i 称为虚数单位,当 b = 0 时,z 就是常见的实数,当 a = 0 而 b ≠ 0 时,将 z 称之为纯虚数,如果你理解数学概念中的复数概念,这些都很好理解,下面我们来看下复数在 Go 语言中的表示和使用。
在 Go 语言中,复数支持两种类型:complex64
(32 位实部和虚部) 和 complex128
(64 位实部与虚部),对应的表示示例如下,和数学概念中的复数表示形式一致:
Go
var complexValue1 complex64
complexValue1 = 1.10 + 10i // 由两个 float32 实数构成的复数类型
complexValue2 := 1.10 + 10i // 和浮点型一样,默认自动推导的实数类型是 float64,所以 complexValue2 是 complex128 类型
complexValue3 := complex(1.10, 10) // 与 complexValue2 等价
使用 complex
来创建复数类型的变量
Go
var score complex64 = complex(1, 2)
var number complex128 = complex(11.11, 22.22)
fmt.Printf("score: %v 类型: %T\n", score, score)
fmt.Printf("number值: %v 类型: %T", number, number)
/* 输出
score值: (1+2i) 类型: complex64
number值: (11.11+22.22i) 类型: complex128
*
获取复数类型的 实部
和 虚部
Go
var score complex64 = complex(1, 2)
var number complex128 = complex(11.11, 22.22)
fmt.Printf("score 实部: %v, 虚部: %v\n", real(score), imag(score))
fmt.Printf("number 实部: %v, 虚部: %v", real(number), imag(number))
/*输出
score 实部: 1, 虚部: 2
number 实部: 11.11, 虚部: 22.22
*
对于一个复数 z = complex(x, y)
,就可以通过 Go 语言内置函数 real(z)
获得该复数的实部,也就是 x
,通过 imag(z)
获得该复数的虚部,也就是 y
。
复数支持和其它数字类型一样的算术运算符。当你使用 ==
或者 !=
对复数进行比较运算时,由于构成复数的实数部分也是浮点型,需要注意对精度的把握。
更多关于复数的函数,请查阅 math/cmplx 标准库的文档。如果你对内存的要求不是特别高,最好使用 complex128
作为计算类型,因为相关函数大都使用这个类型的参数。