分析浮点数精度损失

什么是精度损失?

精度损失指的是在数据存储中想要存储的的值与实际值不一致,比方说你想要存储在数据库存 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 位,当我们的小数部分转换出现无限循环时,超过了尾数的长度,那就要舍弃,那剩余的二进制和原先的数就会不一致了。

相关推荐
Boilermaker199217 小时前
[MySQL] 服务器架构
数据库·mysql·架构
qualifying19 小时前
MySQL——表的操作
数据库·mysql
学Linux的语莫19 小时前
mysql主从同步(复制)搭建
数据库·mysql
MySQL实战20 小时前
MySQL 在哪些场景下不会写 binlog
mysql
苹果酱056720 小时前
解决linux mysql命令 bash: mysql: command not found 的方法
java·vue.js·spring boot·mysql·课程设计
问道飞鱼21 小时前
【数据库知识】MySQL 多表关联高效实现指南:场景化方案与底层原理
数据库·mysql·多表关联
dblens 数据库管理和开发工具21 小时前
MySQL :5.7与8.0版创建用户与授权、密码认证插件、角色、密码过期策略
数据库·mysql·dblens·mysql创建用户·mysql设置密码·mysql用户授权
卡尔特斯21 小时前
Mysql ERROR 1524 (HY000): Plugin 'mysql_native_password' is not loaded
mysql
小韩博1 天前
小迪第42课:PHP应用&MYSQL架构&SQL注入&跨库查询&文件读写&权限操作
sql·mysql·网络安全·架构·php
北极糊的狐1 天前
若依报错org.springframework.dao.DataIntegrityViolationException
数据库·mysql