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。

相关文章
|
2月前
|
监控 安全 Java
Go语言学习笔记(一)
Go语言学习笔记(一)
106 1
|
3月前
|
缓存 Go API
Go 实现一个支持多种过期、淘汰机制的本地缓存的核心原理
本文旨在探讨实现一个支持多种 过期、淘汰 机制的 go 本地缓存的核心原理,我将重点讲解如何支持多样化的过期和淘汰策略。
73 0
|
4月前
|
存储 安全 Java
Go 基础数据结构的底层原理(slice,channel,map)
Go 基础数据结构的底层原理(slice,channel,map)
53 0
|
6月前
|
存储 设计模式 编译器
详解Go语言类型与接口关系:从原理到应用全解密
详解Go语言类型与接口关系:从原理到应用全解密
41 0
|
6月前
|
存储 Java 编译器
Go函数解密:底层工作原理
Go函数解密:底层工作原理
71 0
|
6月前
|
Go
Go语言浮点数完全手册 float32和float64一文掌握!
Go语言浮点数完全手册 float32和float64一文掌握!
537 0
|
6月前
|
测试技术 Go 开发工具
100天精通Golang(基础入门篇)——第3天:Go语言的执行原理及常用命令、编码规范和常用工具
100天精通Golang(基础入门篇)——第3天:Go语言的执行原理及常用命令、编码规范和常用工具
149 1
|
1月前
|
存储 算法 Java
Go语言GC(垃圾回收)的工作原理
【2月更文挑战第23天】
28 0
|
1月前
|
存储 分布式计算 算法
GO学习笔记之表达式
GO学习笔记之表达式
33 1
|
1月前
|
存储 编译器 Go
GO语言学习笔记
GO语言学习笔记
23 1