最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 『面试的底气』—— 0.1+0.2等于0.3吗|牛气冲天新年征文

    正文概述 掘金(红尘炼心)   2021-02-10   564

    前言

    去互联网金融或电商行业的公司面试时,一般都会遇到类似“0.1+0.20.1+0.20.1+0.2等于0.30.30.3吗?”这道题,对于非科班出身的前端人是一道送命题,有些知道0.1+0.20.1+0.20.1+0.2不等于0.30.30.3,但是继续深问为什么,就无法很清晰地回答。

    本专栏总结一下回答 0.1+0.20.1+0.20.1+0.2不等于0.30.30.3 的思路,在回答之前要先弄清楚0.1+0.20.1+0.20.1+0.2的计算过程。

    0.1+0.2的计算过程计算过程

    1、十进制转成二进制

    在JS内部所有的计算都是以二进制方式计算的。 所以运算0.1+0.20.1+0.20.1+0.2时要先把0.10.10.1和0.20.20.2从十进制转成二进制。

    • 0.1转化成二进制的算法:

      0.1*2=0.2======取出整数部分0

      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*2=0.4======取出整数部分0

      0.4*2=0.8======取出整数部分0

      0.8*2=1.6======取出整数部分1

      0.6*2=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.6*2=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.10.10.1和0.20.20.2转成的二进制是无穷的。另外在现代浏览器中是用浮点数形式的二进制来存储二进制,所以还要把上面所转化的二进制转成浮点数形式的二进制。

    2、转成浮点数

    浮点数分为单精度对应32位操作系统和双精度对应64位操作系统。目前的操作系统大多是64位操作系统,故这里只解释一下二进制如何转成双精度浮点数的二进制。

    双精度浮点数用1位表示符号位,11位表示指数位,52位表示小数位,如下图所示:

    『面试的底气』—— 0.1+0.2等于0.3吗|牛气冲天新年征文

    • 符号位:正数为0,负数为1;

    • 指数位:阶数+偏移量,阶数是:2e112^{e-1}-12e−1−1,eee为阶码的位数。偏移量是把小数点移动到整数位只有111时移动的位数,正数表示向左移,负数表示向右移;

    • 小数位:即二进制小数点后面的数。

    接下来把0.10.10.1转成的二进制0.0001100110011001......0.0001 1001 1001 1001......0.0001100110011001......转成浮点数形式的二进制。

    • 先要把小数点移动到整数位只有111,要向右移动4位,故偏移量为4-4−4,通过指位数的计算公式211114=10192^{11-1}-1-4 = 1019211−1−1−4=1019,把101910191019转成二进制为111111101111111110111111111011,不够11位要补零,最终得出指位数为011111110110111111101101111111011;

    • 小数位为100110011001......1001 1001 1001......100110011001......,因为小数位只能保留52位,第53位为1故进1。

    转换结果如下图所示:

    『面试的底气』—— 0.1+0.2等于0.3吗|牛气冲天新年征文

    同理,再把0.20.20.2转成的二进制0.00110011 00110011......0.0011 0011 0011 0011......0.00110011 00110011......转成浮点数形式的二进制,转换结果如下图所示:

    『面试的底气』—— 0.1+0.2等于0.3吗|牛气冲天新年征文

    浮点数相加

    浮点数相加时,需要先比较指位数是否一致,如果一致则小数位直接相加,如果不一致,要先把指位数调成一致的,指位数小的向大的调整。

    为了行文方便,把0.1转成的浮点数称为为0.1,把0.2转成的浮点数称为0.2。

    0.1的指数位是101910191019,0.2的指数位是102010201020。故要把0.1的指数位加1,即把0.1的小数点向左移动1位,另外浮点数的整数位固定为1,过程如下所示

    1.1001100110011001100110011001100110011001100110011010   原先
    0.11001100110011001100110011001100110011001100110011010  移动后  
    0.1100110011001100110011001100110011001100110011001101   将小数的第53位舍去,因为为0故不需进1
    

    导致0.1的小数位变成如下所示: 『面试的底气』—— 0.1+0.2等于0.3吗|牛气冲天新年征文

    现在0.1和0.2的指数位相同了,把小数位直接相加。

        1100110011001100110011001100110011001100110011001101 0.1的小数位
    +   1001100110011001100110011001100110011001100110011010 0.2的小数位
    =  10110011001100110011001100110011001100110011001100111
    

    会发现现在的小数位多出了一位,超出了52位,故要把小数位最后一位截掉,小数位最后一位是1,故要进1,如下所示:

    10110011001100110011001100110011001100110011001100111
    1011001100110011001100110011001100110011001100110100
    

    截掉小数位的最后一位相当把小数点向左移了一位,故指数位要加1,此时的指数是0.2的指数102110211021,加1后变成102110211021,转成二进制为011111111010111111110101111111101 ,那么相加后的浮点数如下所示:

    『面试的底气』—— 0.1+0.2等于0.3吗|牛气冲天新年征文

    浮点数转成十进制

    二进制浮点数计算结束后,把结果(二进制的浮点数)转成十进制,其转换公式为 (1)s2e1023(1+i=152(Mi2i)){(-1)}^s*2^{e-1023}*(1+\sum_{i=1}^{52}(M_i*2^{-i}))(−1)s∗2e−1023∗(1+∑i=152​(Mi​∗2−i)) ,s是符号位为0或1,e为浮点数指数位转成十进制的值,i表示小数位从左到右的位数,第一位i=1i=1i=1,MiM_iMi​表示每一位的值为0或1。

    那么按着公式把二进制的浮点数转成十进制:

    (1)022(1+121+0+123+124+...)({-1})^0*2^{-2} * (1+ 1*2^-1 + 0 + 1*2^-3 + 1*2^-4 + ...)(−1)0∗2−2∗(1+1∗2−1+0+1∗2−3+1∗2−4+...)

    结果如下所示:

    0.30000000000000004440892098500626161694526672363281250.30000000000000004440892098500626161694526672363281250.3000000000000000444089209850062616169452667236328125

    由于精度问题,只取到0.300000000000000040.300000000000000040.30000000000000004。

    答案

    0.1+0.20.1+0.20.1+0.2不等于0.30.30.3,因为在0.1+0.20.1+0.20.1+0.2的计算过程中发生了两次精度丢失。第一次是在0.10.10.1和0.20.20.2转成双精度二进制浮点数时,由于二进制浮点数的小数位只能存储52位,导致小数点后第53位的数要进行为1则进1为0则舍去的操作,从而造成一次精度丢失。第二次在0.10.10.1和0.20.20.2转成二进制浮点数后,二进制浮点数相加的过程中,小数位相加导致小数位多出了一位,又要让第53位的数进行为1则进1为0则舍去的操作,又造成一次精度丢失。最终导致0.1+0.20.1+0.20.1+0.2不等于0.30.30.3。

    拓展

    若你回答出来,面试官还可能继续问你:“0.1+0.20.1+0.20.1+0.2不等于0.30.30.3会引起那些BUG?”

    可以这样回答:“ 会引起统计页面展示错乱的BUG,还有300.01300.01300.01优惠300300300元后,支付金额不足0.010.010.01元等类似的BUG。”

    还可能继续问道:“怎么解决0.1+0.20.1+0.20.1+0.2不等于0.30.30.3这个问题”。

    可以这样回答:“可以用Math.js数学计算库来解决,或者用toFixed()给计算结果四舍五入,但是toFixed()在chrome或者火狐浏览器下四舍五入也有精度误差。可以用Math.round来解决精度误差,比如要把2.552.552.55四舍五入保留111位小数,先把2.55102.55*102.55∗10得到25.525.5%25.5,再用Math.round取整25.525.5%25.5,会得到252525,再把25÷1025÷1025÷10得到2.52.52.5,就这样间接实现了四舍五入。可以用Math.pow来做个简单的封装Math.round(Math.pow(10, m) * number) / Math.pow(10, m),其中number是要四舍五入的数,m是保留几位小数。


    起源地下载网 » 『面试的底气』—— 0.1+0.2等于0.3吗|牛气冲天新年征文

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元