js小数浮点数操作出现的精度问题的原因以及解决方法

更新日期: 2019-03-28阅读: 2k标签: 浮点数

一、问题的引入

今天在看基础js文章的时候发现了一个浮点数的精度问题,当打印小数相加的时候有时候会出现数值不准确的情况,如果是在做一些需要数据精度要求较高的工作的时候稍有不慎就会出现问题

console.log(0.1+0.1) //0.2
console.log(0.1+0.2) //0.30000000000000004(精度最高保留到17位)

查阅资料之后,发现是因为像 0.1+0.2这样的操作对于计算机来说转换为二进制之后将是两个无限循环的数。而对于计算机而言是不允许有无限的,进行四舍五入之后双精度浮点数保留52位,结果为0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100转为十进制就是0.30000000000000004


二、基本解决方法

【1】利用toFixed(digits)
toFixed函数在digits参数存在的情况下返回一个所给数值的定点数表示法的字符串(digits要在0-20之间,默认为0)。我们可以利用该函数限定返回数值的位数,从而达到提高精度的效果。

 var floatNum=0.1+0.2;
 console.log(floatNum);   //0.30000000000000004
 console.log(floatNum.toFixed(2)) //0.30
 
 console.log(1.35.toFixed(1)) //1.4
 console.log(1.33335.toFixed(4)); //1.3334 

在这里我们看上去是解决了问题,但是和直接进行相加一样有时候toFixed 也会有问题

    Chrome:
    console.log(1.55.toFixed(1));// 1.6
    console.log(1.555.toFixed(2));//1.55
    console.log(1.5555.toFixed(3));//1.556

关于这个问题,MDN上函数的描述为 “该数值在必要时进行四舍五入,另外在必要时会用 0 来填充小数部分,以便小数部分有指定的位数。”至于是怎么定义“必要”的,不同的浏览器会有不同的情况。

    IE:
    console.log(1.55.toFixed(1)); //1.6
    console.log(1.555.toFixed(2)); //1.56
    console.log(1.5555.toFixed(3)); //1.556

【2】重写toFixed函数

思路为放大原有的数据,利用整数的整除来避免精度丢失

    function toFixed(num, s) {
        if(typeof s==='undefined'){
            return parseInt(num)+'';
        }
        if(typeof s!=='number'){
            return '请正确输入保留位数(数字)';
        }
        var times = Math.pow(10, s);
        var newNum = num * times+0.5;//加0.5是为了实现四舍五入中"入"的那0.5
        newNum = parseInt(newNum) / times;
        return newNum + ''//toFixed返回的是字符类型的数据
    }
    console.log(toFixed(1.5)) //2
    console.log(toFixed(1.55,1)) //1.6
    console.log(toFixed(1.555,2))  //1.56
    console.log(toFixed(1.5555,3)) //1.556

【3】基础运算自实现

    function getDecimalLength(num){ //获取小数位长度
        let length=0;
        try{
            length =String(num).split('.')[1].length
        }catch(e){
            //TODO handle the exception
        }
        return length;
    }
    
    function getBeishu(num1,num2){  //获取放大倍数
        let num1DecimalLength=getDecimalLength(num1);
        let num2DecimalLength=getDecimalLength(num2);
        let longer=Math.max(num1DecimalLength,num2DecimalLength);
        return Math.pow(10,longer);
    }
    
    
    //加减乘除算法
    function add(num1,num2){
        let beishu=getBeishu(num1,num2);
        return (num1*beishu+num2*beishu)/beishu;
    }
    
    function sub(num1,num2){
        let beishu=getBeishu(num1,num);
        return (num1*beishu-num2*beishu)/beishu;
    }
    
    function mul(num1,num2){
        let num1DecLen=getDecimalLength(num1);
        let num2DecLen=getDecimalLength(num2);
        let num1toStr=String(num1);
        let num2toStr=String(num2);
        return Number(num1toStr.replace('.',''))*Number(num2toStr.replace('.',''))/Math.pow(10,num1DecLen+num2DecLen)
    }
    
    
    function dev(num1,num2){
        let num1DecLen=getDecimalLength(num1);
        let num2DecLen=getDecimalLength(num2);
        let num1toStr=String(num1);
        let num2toStr=String(num2);
        return Number(num1toStr.replace('.',''))/Number(num2toStr.replace('.',''))/Math.pow(10,num1DecLen-num2DecLen)
    }

链接: https://fly63.com/article/detial/2575

js浮点数精度丢失问题_如何解决js中浮点数计算不精准?

理解javascript中浮点数计算不精准的原因,如何解决浮点数的四则运算(加减乘除)。js中除了toFixed方法以外的实现方法总汇

js浮点数运算封装

项目中用到浮点数,Int 等 js中 Number类型比较多, 加上牵涉到财务软件, 前台js运算等。 有时候会出现精确度的问题 , 公共方法中有好事者写的方法。 此处拿来借鉴。

js的数值范围

JavaScript能表示并进行精确算术运算的整数范围为:正负2的53次方,也即从最小值-9007199254740992到最大值+9007199254740992之间的范围;对于超过这个范围的整数,JavaScript依旧可以进行运算,但却不保证运算结果的精度。

JS浮点数值问题

JS能够正确表示的整数,JS里面尽管能够正确表示的数值量在2^53,但是位运算能够正常运算的范围却依然是32位,第一位为符号位,所以是2^31,转成10位的边界值是(2147483648)。

Js在计算浮点数(小数)不准确,解决方案

实现简单,便于做加减乘除使用,由于项目临时要用记录下,如需要更加复杂的计算类库,可以考虑 math.js等知名类库;代码,使用方法如下

浮点数运算的误差

在 JavaScript 中整数和浮点数都属于number 数据类型,所有数字都是使用64位浮点数形式储存,遵循IEEE-754双精度标准存储,即便整数也是如此。 所以我们在打印 1.00 这样的浮点数的结果是 1 而非 1.00。

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!