分析浮点数精度损失

什么是精度损失?

精度损失指的是在数据存储中想要存储的的值与实际值不一致,比方说你想要存储在数据库存 123.456,但是存到数据库里就变成了 123.4560013。下图 a 字段的类型设置的是 float,当我们输入 123.456 并保存到数据库之后就变成了123.4560013,这就发生了精度损失。

为什么会发生精度损失?

计算机使用二进制存储数据,由于二进制自身局限性,导致其无法精确的表示所有小数,具体说就是计算机在将小数转换为二进制时,转换后的位数可能很长,超过了存储位数,这就就必须舍弃多余的位数,从而造成了实际值与存储值不一致,产生精度损失。

十进制转换为二进制

对于十进制整数转换为二进制,采用"除二取余,逆序排列"法,假设我们将 127 转换成二进制,具体过程如下:

十进制小数转换为二进制

那么十进制小数转换成二进制小数采用"乘二取整,顺序排列"法,假设我们将 0.625 转换成二进制数,具体过程如下:

但是 0.625 是一个特例,当我们用 0.1 用同样的算法去计算时,我们会发现 0.1 的二进制小数会出现无限循环的情况,在这种情况下计算机就无法使用二进制数精确地表示 0.1 了。

那为了解决这个问题,就有了 IEEE-754 规范,需要注意的是这个规范并没有解决小数无法精确表示的问题,只是提出了一种使用近似值表示小数的方式,并引入了精度的概念。

一个浮点数 a 由两个数 m 和 e 表示:,m 是尾数,e 是指数。

Float的存储结构举例

float 存储遵循 IEEE 754标准,只占 4 个字节,总共 32 位,叫作单精度浮点数,内存结构如下图所示:

符号位:0 表示正数,1 表示负数

指数位:共 8 位,存储时应加上 127

尾数位:共 23 位

我们以 13.625 为例转换出它的内存结构:

  • 采用"除二取余,逆序排列"法,将整数部分的13转换为二进制得:1101。
  • 采用"乘二取整,顺序排列"法,将小数部分的0.625转换为二进制得:0.101。
  • 合二为一得:1101.101。
  • 将小数点向左移动,使小数点前只有一位,且保证这位为1得:,尾数为:1.101101,指数为:3。
  • 将指数加上 127,变为 130,将 130 转换为二进制得:10000010,此时指数正好为 8 位,如果不足 8 位前面补 0。
  • 由于尾数 1.101101 前面固定为 1,存储时可以省略,得到尾数为 101101,不足 23 位,末尾补17 个 0 得:10110100000000000000000。
  • 按照格式:符号位 + 指数位 + 尾数位得:0 + 10000010 + 10110100000000000000000 = 01000001 01011010 00000000 00000000

我们再回过头来分析,因为我们规定了尾数的长度为 23 位,当我们的小数部分转换出现无限循环时,超过了尾数的长度,那就要舍弃,那剩余的二进制和原先的数就会不一致了。

相关推荐
随风飘的云30 分钟前
mysql的innodb引擎对可重复读做了那些优化,可以避免幻读
mysql
于眠牧北3 天前
MySQL的锁类型,表锁,行锁,MVCC中所使用的临键锁
mysql
Turnip12025 天前
深度解析:为什么简单的数据库"写操作"会在 MySQL 中卡住?
后端·mysql
加号36 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏6 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
WeiXin_DZbishe6 天前
基于django在线音乐数据采集的设计与实现-计算机毕设 附源码 22647
javascript·spring boot·mysql·django·node.js·php·html5
爱可生开源社区6 天前
MySQL 性能优化:真正重要的变量
数据库·mysql
小马爱打代码6 天前
MySQL性能优化核心:InnoDB Buffer Pool 详解
数据库·mysql·性能优化
风流 少年6 天前
mysql mcp
数据库·mysql·adb
西门吹雪分身6 天前
mysql之数据离线迁移
数据库·mysql