前言
浮点数,相信无论是初学代码的小码农或者运筹帷幄的技术大牛,都不会对这个概念陌生,在百科词条下,我们可以看到浮点数是这么定义的
浮点数,是属于有理数中某特定子集的数的数字表示,在计算机中用以近似表示任意某个实数。具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到,这种表示方法类似于基数为10的科学计数法。
乍一看,浮点数好像从概念上来说显得很高深莫测,实际上,浮点数比你想象中的秘密还要多的多嘞
本篇文章带大家深入了解浮点数,包括其精度丢失的底层原因及规避方法,助力大家写出更稳健的代码
先看一个简单案例
问题1:0.5连加10次等于几?
相信有人会说了,这题我奶奶来了都会,不就是5嘛。
那我们就用IDE代码测试用例验证跑一下
果然是5,大家应该都答对第一题了吧,鼓鼓掌,别急,我们接下来看第二个问题
问题2:0.1连加10次等于几?
有人要说了,这不是在糊弄小孩子嘛,我用脚趾头都能算出来是1,那么我们用代码跑一下呢? 这时候我们将会神奇的发现,算出来的数字居然变成了0.999999999...
这里面到底暗藏了什么玄鸡(坤)呢?别急,下面会为你一一揭晓
整数是怎么存储的?
在了解浮点数之前,我们可以先了解下整数是怎么存储的,来做一个铺垫
在计算机中,正整数是可统一视为以原码的方式来存储的
负整数在计算机中是以补码的形式存储的
为什么会有区别呢?其实就是定义一套规则给到计算机能够正确的用二进制算出来对应的算法
对于原码,反码,补码的概念,大家可以自行查阅~,这里只做一个简单介绍
不同的数在计算机中存储的二进制形式是不一样的,那么小数又是怎么存储的呢?
小数是怎么存储的?
定义
这里我们简单介绍一个标准------IEEE 754标准,对于小数的存储,都是按这标准得出来的。
IEEE二进制浮点数算术标准(IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number)),一些特殊数值(无穷(Inf)与非数值(NaN)),以及这些数值的"浮点数运算符";它也指明了四种数值舍入规则和五种例外状况(包括例外发生的时机与处理方式)。
双精度浮点数用64位、单精度浮点数用32位来表示全体小数
浮点数是指用符号、尾数、基数和指数这四部分来表示的小数 基数默认是2,实际只用符号、尾数、指数这三部分即可
尾数
尾数部分使用将小数点前面的值固定为1的正则表达式
第一位的1在实际的数据中不保存,可节省一位从而表示更多的数据范围
指数
指数部分使用EXCESS系统 将指数部分表示范围的中间值设为0,使得负数不需要用符号表示
演示
这里完整演示十进制小数转为计算机存储的二进制数据的流程
利用IEEE标准的格式存储小数的好处?
精度丢失的原因
通过以上的解释,大家想必也知道,为什么问题2会丢失精度。
其根本原因是因为
有一些十进制的小数无法转换为二进制数
- 二进制是连续的,可表示的十进制数是非连贯的
- 舍入规则会引入舍入误差
怎么解决精度问题?
知道会有精度问题以后,我们该如何解决这个精度问题呢?在日常开发中,我们只要牢记一点就好 把小数转换成整数来运算
- 按实际业务场景来设计精确到小数点后几位
- 整数运算只要不溢出就不会有问题
具体例子可以如下, 假设我们设计一个抽奖的权重算法。
上面的checkProbablity由于相加为浮点数,导致算法本身会有问题。 下面的checkProbablity将概率强制转为int后,实际不会有问题。
最后的最后
浮点数用好了,在日常开发遇到处理浮点数这类情况我们就可以游刃有余,从而避免一些业务上事故。 浮点数,你学废了吗?