分析浮点数精度损失

什么是精度损失?

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

相关推荐
切糕师学AI16 分钟前
SQL中的函数索引/表达式索引
数据库·sql·mysql·postgresql·oracle
S_h_a_43 分钟前
八股-Mysql 基础篇(1)
数据库·mysql
Dxy123931021643 分钟前
MySQL的GROUP_CONCAT函数详解
数据库·mysql
小白银子3 小时前
零基础从头教学Linux(Day 60)
linux·数据库·mysql·oracle
憋问我,我也不会3 小时前
MYSQL 命令
数据库·mysql
无泡汽水4 小时前
MySQL入门练习50题
数据库·mysql
云外天ノ☼5 小时前
待办事项全栈实现:Vue3 + Node.js (Koa) + MySQL深度整合,构建生产级任务管理系统的技术实践
前端·数据库·vue.js·mysql·vue3·koa·jwt认证
⑩-6 小时前
如何保证Redis和Mysql数据缓存一致性?
java·数据库·redis·mysql·spring·缓存·java-ee
wangjialelele7 小时前
MySQL操作库
数据库·mysql·oracle
C++chaofan8 小时前
MyBatis - Plus学习笔记
java·spring boot·笔记·后端·mysql·架构·mybatis