浮点数相加 精度问题 配合PAT A1016食用

来自:掘金,作者:红尘炼心
前言
去互联网金融或电商行业的公司面试时,一般都会遇类似“ 0.1+0.2 等于 0.3吗?”这道题,对于非科班出身的前端人是一道送命题,有些知道 0.1+0.2 不等于 0.3,但是继续深问为什么,就无法很清晰地回答。
本专栏总结一下回答 0.1+0.2 不等于 0.3 的思路,在回答之前要先弄清楚 0.1+0.2 的计算过程。
0.1+0.2 的计算过程计算过程
1、十进制转成二进制
在JS内部所有的计算都是以二进制方式计算的。所以运算 0.1+ 0.2 时要先把 0.1和 0.2 从十进制转成二进制。
0.1转化成二进制的算法:
0.12=0.2取出整数部分0
0.2*2=0.4
取出整数部分0
0.4
2=0.8取出整数部分0
0.8*2=1.6
取出整数部分1
0.62=1.2取出整数部分1
接下来会无限循环
0.2*2=0.4
取出整数部分0
0.4
2=0.8取出整数部分0
0.8*2=1.6
取出整数部分1
0.62=1.2取出整数部分1
所以0.1转化成二进制是:0.0001 1001 1001 1001…
0.2转化成二进制的算法:
0.2*2=0.4
取出整数部分0
0.4
2=0.8取出整数部分0
0.8*2=1.6
取出整数部分1
0.62=1.2取出整数部分1
接下来会无限循环
0.2*2=0.4
取出整数部分0
0.4
2=0.8取出整数部分0
0.8*2=1.6
取出整数部分1
0.6*2=1.2======取出整数部分1
所以0.2转化成二进制是:0.0011 0011 0011 0011…
这里要注意 0.1 和 0.2 转成的二进制是无穷的。另外在现代浏览器中是用浮点数形式的二进制来存储二进制,所以还要把上面所转化的二进制转成浮点数形式的二进制。
2、转成浮点数
浮点数分为单精度对应32位操作系统和双精度对应64位操作系统。目前的操作系统大多是64位操作系统,故这里只解释一下二进制如何转成双精度浮点数的二进制。
双精度浮点数用1位表示符号位,11位表示指数位,52位表示小数位,如下图所示:
符号位:正数为0,负数为1;
指数位:+,阶数是:,e为阶码的位数。偏移量是把小数点移动到整数位只有1时移动的位数,正数表示向左移,负数表示向右移;
小数位:即二进制小数点后面的数。
先要把小数点移动到整数位只有1,要向右移动4位,故偏移量为−4,通过指位数的计算公式,把1019转成二进制为1111111011,不够11位要补零,最终得出指位数为01111111011;
小数位为100110011001… ,因为小数位只能保留52位,第53位为1故进1。
转换结果如下图所示:
同理,再把 0.2 转成的二进制0.0011 0011 0011 0011… 转成浮点数形式的二进制,转换结果如下图所示:
浮点数相加
浮点数相加时,需要先比较指位数是否一致,如果一致则小数位直接相加,如果不一致,要先把指位数调成一致的,指位数小的向大的调整。
为了行文方便,把0.1转成的浮点数称为为0.1,把0.2转成的浮点数称为0.2。
0.1的指数位是1019 ,0.2的指数位是1020 。故要把0.1的指数位加1,即把0.1的小数点向左移动1位,另外浮点数的整数位固定为1,过程如下所示
导致0.1的小数位变成如下所示:
图片
现在0.1和0.2的指数位相同了,把小数位直接相加。
会发现现在的小数位多出了一位,超出了52位,故要把小数位最后一位截掉,小数位最后一位是1,故要进1,如下所示:
浮点数转成十进制
二进制浮点数计算结束后,把结果(二进制的浮点数)转成十进制,其转换公式为 ,s是符号位为0或1,e为浮点数指数位转成十进制的值,i表示小数位从左到右的位数,第一位 i=1 ,表示每一位的值为0或1。
那么按着公式把二进制的浮点数转成十进制:
结果如下所示:
0.3000000000000000444089209850062616169452667236328125
由于精度问题,只取到0.30000000000000004。
答案
0.1+0.2 不等于 0.3 ,因为在 0.1+0.2 的计算过程中发生了两次精度丢失。第一次是在 0.1 和 0.2 转成双精度二进制浮点数时,由于二进制浮点数的小数位只能存储52位,导致小数点后第53位的数要进行为1则进1为0则舍去的操作,从而造成一次精度丢失。第二次在 0.1 和 0.2 转成二进制浮点数后,二进制浮点数相加的过程中,小数位相加导致小数位多出了一位,又要让第53位的数进行为1则进1为0则舍去的操作,又造成一次精度丢失。最终导致 0.1+0.2 不等于0.3 。
拓展
若你回答出来,面试官还可能继续问你:“ 0.1+0.2 不等于 0.3 会引起那些BUG?”
可以这样回答:“ 会引起统计页面展示错乱的BUG,还有 300.01 优惠300 元后,支付金额不足0.01 元等类似的BUG。”
还可能继续问道:“怎么解决 0.1+0.2 不等于 0.3 这个问题”。
可以这样回答:“可以用Math.js数学计算库来解决,或者用给计算结果四舍五入,但是在chrome或者火狐浏览器下四舍五入也有精度误差。可以用来解决精度误差,比如要把 2.55 四舍五入保留 1 位小数,先把 2.55∗10 得到 25.5 ,再用取整25.5 ,会得到25,再把 25÷10 得到 2.5 ,就这样间接实现了四舍五入。可以用来做个简单的封装,其中是要四舍五入的数,是保留几位小数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值