Go学习笔记-Go 浮点数设计原理

简介: Go学习笔记-Go 浮点数设计原理

1.浮点数陷阱

  • 在Go语言中,整数数据类型无法用int、int32、int64 这些类型来表示小数,浮点数类型可以在程序中高效表示小数,但在计算的过程中可能丢失精度。
  • 浮点数计算丢失经度举例:
package main
import "fmt"
func main()  {
    var a float64 = 0.6
    var b float64 = 0.3
    fmt.Println(a + b)
}

运行结果如下图:

2.定点数与浮点数

  • 计算机底层是通过二进制的形式存储数据,很多小数表示成二进制后是近似且无限的,例如十进制的 0.1,如果转化为二进制结果是 0.0001100110011001100…,它是一个无限循环的数字,而计算机底层是有限的地址空间,无法存储无限的数字。
  • 最简单表示小数的办法就是使用 定点表示法,可以用固定的大小来表示整数,剩余的部分表示小数。
  • 例如用一个 16 位的无符号整数(uint16),可以用前面的 8 位存来储整数部分,后面的 8 位来存储小数部分,这样的话整数的部分最大表示的数字是 2 的 8 次方,即 256,最小表示的数字是 0,8 位表示的数字范围是 0~256,小数部分可以表示的范围是 1/256~1。
  • 在有些情况下有可能整数的部分占位很长,小数的部分占位却很短,为了解决这中问题,可以使用浮点来存储数据。
  • 用科学计数法来表示小数,例如使用 5 位的数字就可以表示的范围是 0.00~9.99×10^99 的数字,通过调整科学计数法后边 10 的指数可表示不同大小十进制的数,计算机浮点数设计的灵感和这个类似。

3.IEEE-754浮点数标准

  • IEEE-754 规范是以 2 为底数的指数表示小数,和十进制树的科学计数法很类似。
  • 例如 0.025 可以使用 1.6✖️2^-6 表示,其中 1.6 为系数,2 为底数,-6 为指数。
  • 十进制数表示法举例:
十进制数字 科学计数法 指数表示法 系数 底数 指数
350000 3.5e+5 3.5✖️10^5 3.5 10 5
313.232 3.13232e+2 3.13232✖️10^2 3.1323210 10 2
-0.02424 2.424e-2 2.424✖️10^-2 2.424 10 -2
  • 位数越多,可以存储数字范围也越大,大部分硬件浮点数单元支持的32位的单精度浮点数与64位的双精度浮点数。
  • 单精度与双精度浮点数格式:
精度 符号位 指数位 小数位 偏移量
单精度(32 Bits) 1 Bit (31) 8 Bits (高位30~低位23) 23 Bits (高位22~低位00) 127
双精度(64 Bits) 1 Bit (63) 11 Bits (高位62~低位52) 52 Bits (高位51~低位00) 1023

Tips:最开头的 1 位是符号位, 1 表示负数,0 表示正数,符号位后边的紧接着是指数位,单精度的指数位长度是 8,双精度指数位长度是 11,指数位存储了指数加上偏移量的值,例如当指数为-8 时,实际存储的值为 -8+127=119,指数位后边紧接着是小数位,单精度的小数位长度是 23,双精度的小数位长度是 52,小数位存储系数中小数位的准确值或最接近的值,是 0 到 1 之间的数。

  • 0.085单精度浮点数表示
符号 指数部分(123) 小数部分(.36)
0 0111 1011 010 1110 0001 0100 0111 1011

Tips:指数部分 -4 + 127,即 123。

4.小数部分计算:

  • 小数部分存储的可能是系数的近似值而不是准确值。
  • 小数位的每一位代表的都是2的幂,并且指数依次减少1。
  • 以 0.085 的浮点表示法中系数的小数部分(0.36) 为例,对应的二进制数为 010 1110 0001 0100 0111 1011:
对应的整数 转化为分数 转化为十进制小数 各位的总和
2 4 1/4 0.25 0.25
4 16 1/16 0.0625 0.3125
5 32 1/32 0.03125 0.34375
6 64 1/64 0.015625 0.359375
11 2048 1/2048 0.00048828125 0.35986328125
13 8192 1/8192 0.0001220703125 0.3599853515625
17 131072 1/131072 0.00000762939453 0.35999298095703
18 262144 1/262144 0.00000381469727 0.3599967956543
19 524288 1/524288 0.00000190734863 0.35999870300293
20 1048576 1/1048576 0.00000095367432 0.35999965667725
对应的整数 转化为分数 转化为十进制小数 各位的总和
22 4194304 1/4194304 0.00000023841858 0.35999989509583
23 8388608 1/8388608 0.00000011920929 0.36000001430512
  • Go语言标准库的 math 包提供了许多有用的计算函数,float32 类型可以以字符串的形式打印出单精度浮点数的二进制值。
  • 判断浮点数为整数的重要思路是指数能够弥补小数部分(即指数的值大于或等于小数的位数)。
  • 在十进制数中,1.23×10^2 是整数,而 1.234×10^2 不是整数,因为指数 2 不能弥补 3 个小数位,以 2 为底数的浮点数的判断思路类似:
func IsInt(bits uint32, bias int) {
    exponent := int(bits >> 23) - bias - 23
    coefficient := (bits & ((1 << 23) - 1)) | (1 << 23)
    intTest := (coefficient & (1 << uint32(-exponent) - 1))
    fmt.Printf("\nExponent: %d Coefficient: %d IntTest: %d\n",
        exponent,
        coefficient,
        intTest)
    if exponent < -23 {
        fmt.Printf("NOT INTEGER\n")
        return
    }
    if exponent < 0 && intTest != 0 {
        fmt.Printf("NOT INTEGER\n")
        return
    }
    fmt.Printf("INTEGER\n")
}
 //(1)第一步,计算指数。这里多减去了数字 23,后面会看到其用途,所以在第一个判断中判断条件为 exponent<-23,即比较指数位的值与 0 的大小。
 //(2)第二步,通过位运算的方式计算出小数部分的值。

Tips: 要保证浮点数格式中实际存储的数为整数,一个必要条件就是浮点数格式中指数位的值大于127。指数位的值为127代表指数为0,如果指数位的值大于127,则代表指数大于0,反之则代表指数小于0。

目录
打赏
0
0
0
0
1
分享
相关文章
Go语言的条件控制语句及循环语句的学习笔记
本文是Go语言的条件控制语句和循环语句的学习笔记,涵盖了if语句、if-else语句、if嵌套语句、switch语句、select语句以及for循环和相关循环控制语句的使用方法。
Go语言的条件控制语句及循环语句的学习笔记
Go: struct 结构体类型和指针【学习笔记记录】
本文是Go语言中struct结构体类型和指针的学习笔记,包括结构体的定义、成员访问、使用匿名字段,以及指针变量的声明使用、指针数组定义使用和函数传参修改值的方法。
Go学习笔记-代码调
近年来,人工智能技术飞速发展,Cody作为由Sourcegraph开发的一款AI驱动编码助手,应运而生。它不仅提供代码预测与补全,还能深度理解代码上下文,为开发者提供准确建议,提升编码效率和质量。Cody能识别潜在错误并提出修复建议,缩短调试时间,同时进行智能代码审查,帮助优化代码结构和风格。未来,随着AI技术进步,Cody将不断学习优化,成为开发者不可或缺的伙伴,推动编程领域的创新与发展。
49 0
深入理解 go reflect - 反射基本原理
深入理解 go reflect - 反射基本原理
94 0
深入理解 go sync.Map - 基本原理
深入理解 go sync.Map - 基本原理
65 0
探索Go语言并发模型:原理与实践
本文深入探讨了Go语言的并发模型,包括其设计原理、Goroutine和Channel的基本使用,以及如何在实际项目中高效地应用这些概念来提高程序的性能和可维护性。
|
10月前
|
Go 语言切片如何扩容?(全面解析原理和过程)
Go 语言切片如何扩容?(全面解析原理和过程)
173 2
|
10月前
|
Golang深入浅出之-Go语言中的服务网格(Service Mesh)原理与应用
【5月更文挑战第5天】服务网格是处理服务间通信的基础设施层,常由数据平面(代理,如Envoy)和控制平面(管理配置)组成。本文讨论了服务发现、负载均衡和追踪等常见问题及其解决方案,并展示了使用Go语言实现Envoy sidecar配置的例子,强调Go语言在构建服务网格中的优势。服务网格能提升微服务的管理和可观测性,正确应对问题能构建更健壮的分布式系统。
514 1
|
10月前
|
Golang深入浅出之-Go语言中的反射(reflect):原理与实战应用
【5月更文挑战第1天】Go语言的反射允许运行时检查和修改结构,主要通过`reflect`包的`Type`和`Value`实现。然而,滥用反射可能导致代码复杂和性能下降。要安全使用,应注意避免过度使用,始终进行类型检查,并尊重封装。反射的应用包括动态接口实现、JSON序列化和元编程。理解反射原理并谨慎使用是关键,应尽量保持代码静态类型。
120 2
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等