IEEE754浮点数

以前每次看到【0.1+0.2结果为什么不是0.3?】这个类问题时候,都是回答  “因为浮点数精度问题导致的”,然后就没有然后了,更深入的回答无法给出。偶然刷到一篇掘金文章解说浮点数问题,因此在这里记录一下。(文章地址:https://juejin.cn/post/6844903474480709640#heading-11

1. 双精度浮点在64位上划分为3段,简称 1-11-52:

1. 1位最高位表示符号位,0表示正,1表示负;

2. 11位表示指数部分

3. 52位表示尾数部分,即有效域部分

举例:十进制数字:5346.8,用科学技术法表示就是:5.3468e3,第一部分是符号,第二部分5.3468是有效数字,第三部分为指数,指数为3。

二进制中尾数部分是52位,但很多十进制的数字不是仅用52位二进制就可以表示完全的,有些十进制转为二进制,它会是无限循环的二进制表示(下面会说十进制怎么转为二进制),所以52各=个坑位是不够的,那超出了怎么办?超出了则会进行 "舍入"规则处理,IEEE754规定了几种舍入规则,但是默认的是舍入到最接近的值,如果“舍”和“入”一样接近,那么取结果为偶数的选择。发生“舍入”操作,则导致精度丢失,所以在浏览器打印【0.1+0.2】结果不会是0.3。

2. 十进制转为二进制

 1. 通过 【.toString()】实现转为二进制

 2. 十进制转为二进制规则

整数转化规则:整数除以2,得商得余数,商继续除以2,直到商为0停止,得到的余数倒顺序一下得到的就为转化后的二进制值。

小数转化规则:小数乘以2,得到的值抽出整数部分,然后整数部分改为0继续乘以2,直到小数部分结果等于0,抽出的整数部分整理在一起,前面加个【0.】,得到的就为转后的二进制值。

         

 3. 解决大多数业务场景的浮点数问题

//注意要传入两个小数的字符串表示,不然在小数转成二进制浮点数的过程中精度就已经损失了
function numAdd(num1/*:String*/, num2/*:String*/) {  
    var baseNum, baseNum1, baseNum2; 
    try { 
        //取得第一个操作数小数点后有几位数字,注意这里的num1是字符串形式的
        baseNum1 = num1.split(".")[1].length; 
    } catch (e) {
        //没有小数点就设为0 
        baseNum1 = 0; 
    } 
    try { 
        //取得第二个操作数小数点后有几位数字
        baseNum2 = num2.split(".")[1].length; 
    } catch (e) { 
        baseNum2 = 0;
    }
    //计算需要 乘上多少数量级 才能把小数转化为整数 
    baseNum = Math.pow(10, Math.max(baseNum1, baseNum2)); 
    //把两个操作数先乘上计算所得数量级转化为整数再计算,结果再除以这个数量级转回小数
    return (num1 * baseNum + num2 * baseNum).toFixed(0) / baseNum; 
};

 

 3. 浏览器中最大的安全数字是 -Math.pow(2,53)+1 到 Math.pow(2,53)-1 

超过了这个安全数字,再进行运算,得到的结果不能保证正确。

 

按照我们的理解,下面的式子都很奇怪,一下false,一下true;

 

 

 

其实,超过了安全数字的,只要加的数字小于 Math.pow(2,x-53) ,等式都会成立。

 

posted @ 2022-10-31 17:27  蛙仔  阅读(68)  评论(0编辑  收藏  举报