js 加减乘除精度修复 2020-12-09

JavaScript011

js 加减乘除精度修复 2020-12-09,第1张

// js小数加减乘除时精度修正

export const floatObj = function () {

    //加法

    function add(a,b){

        return math.number(math.add(math.bignumber(a), math.bignumber(b))) 

    }

    //减法

    function subtract(a,b){

        return math.number(math.subtract(math.bignumber(a), math.bignumber(b)))

    }

    // 乘法

    function multiply(a,b){

        return  math.number(math.multiply(math.bignumber(a), math.bignumber(b)))

    }

    // 除法

    function divide(a,b){

        let num

        a==0||b==0? num = 0 : num = math.number(math.divide(math.bignumber(a), math.bignumber(b)))

        return  num

    }

    //四舍五入并保留n位小数(isRound是否四舍五入)

    function fomatFloat(num,n,isRound){   

        var f = parseFloat(num)

        if(isNaN(f)){

            return false

        }   

        f = Math.round(num*Math.pow(10, isRound?n:n+1))/Math.pow(10, isRound?n:n+1) // n 幂   

        var s = f.toString()

        var rs = s.indexOf('.')

        //判定如果是整数,增加小数点再补0

        if(rs < 0){

            rs = s.length

            s += '.' 

        }

        while(s.length <= rs + n){

            s += '0'

        }

        let _s = s.split(".")[0] + '.' + (s.split(".")[1].substr(0,n))        

        return Number(isRound?s:_s)

    }    

    return {

    add: add,

    subtract: subtract,

    multiply: multiply,

    divide: divide,

    fomatFloat

    }

}()

在新公司的第一个项目是区块链相关的管理后台和交易所,其中就涉及了很多的计算问题。而JavaScript因为存在计算的精度问题,所以直接计算就可能会导致各种各样的bug,为了解决这个问题,就要使用BigNumber.js这个库。

至于为什么JavaScript会有精度问题呢,可以看 这里 。简单来说就是因为: JavaScript中所有的数字(包括整数和小数)都只有一种类型–Number。它的实现遵循IEEE 754标准,使用64位固定长度来表示,也就是标准的double双精度浮点数。它的优点是可以归一化处理整数和小数,节省储存空间。而实际计算的时候会转换成二进制计算再转成十进制。进制转换之后会很长,舍去一部分,计算再转回来,就有了精度误差。

BigNumber.js是一个用于任意精度计算的js库。可以在  官方文档  的console中测试使用。也可以通过npm install bignumber.js --save来安装。然后 import BigNumber from 'bignumber.js' 来引入使用。他的大概原理是将所有数字当做字符串,重新实现了计算逻辑。缺点是性能比原生的差很多。

现在 TC39 已经有一个 Stage 3 的提案 proposal bigint,大数问题有望彻底解决。在浏览器正式支持前,可以使用 Babel 7.0 来实现,它的内部是自动转换成 big-integer 来计算,要注意的是这样能保持精度但运算效率会降低。

具体用法可以参考以下资料:

官方文档

bignumber.js使用记录

BigNumber 讲解

就不再敖述了,下边随便写点常用的方法:

// 转为 bignumberconstx=newBigNumber('123456789.123456789')// 转为 普通数字x.toNumber()// 格式化(小数点)x.toFormat()// '123,456,789.123456789'x.toFormat(3)// '123,456,789.123'// 计算x.plus(0.1)// 加法x.minus(0.1)// 减法x.times(0.1)// 乘法x.div(0.1)// 除法x.mod(3)// 取模/取余// 比较大小x.eq(y)// isEqualTo 的简写,是否相等x.gt(y)// isGreaterThan 的简写,是否大于x.gte(y)// isGreaterThanOrEqualTo 的简写,是否大于等于x.lt(y)// isLessThan 的简写,是否小于x.lte(y)// isLessThanOrEqualTo 的简写,是否小于等于// 取非,改变数字的正负号x.negated()

你试试看 0.1 + 0.7,也是一样有精度丢失的

例如 0.1 + 0.1,在 JS 下用直接输出是 0.2,然而这只是因为默认情况下直接输出的话是有 17 位小数的,然而误差出现在 17 位小数之后。。。

0.1 + 0.1 在 20 位小数的情况下是 0.20000000000000001110

-