【MathJS】入门mathjs最佳实践,解决JS计算精度丢失的问题

前沿

前端的小伙伴,在项目开发中,比如常见的订单模块中,都会进行一些基础的数学运算时,不可避免的就会遇到让人头疼的精度问题。为什么会出现精度问题,这里就略过,本篇重点介绍解决精度问题。

上手撸代码

安装

npm i --save-dev mathjs

在项目中引入mathjs

import * as math from 'mathjs'

加一个consol方法,方便打印

const consol = function () {
  for (let i = 0; i < arguments.length; i++) {
    console.log(arguments[i])
  }
}

Add(加法运算)

// 之前
consol(0.1 + 0.2) // 0.30000000000000004
// 之后
const addNumber = math.format(
  math.chain(math.bignumber(0.1))
    .add(math.bignumber(0.2))
    .done()
)
consol(addNumber) // 0.3

Chaining 链式调用

在实际开发中,需要快速上手,要在最短的时间内解决问题。我的思路就是写一个最简单的用例,搞清楚它的输入和输出,从而正确的理解它的作用。

consol(math.chain(0.1)) // Chain {value: 0.1}

consol(math.chain(0.1).add(0.2)) // Chain {value: 0.30000000000000004}

/*
* 通过在尾部调用一下任意一种方法,返回Chain的value值
* The Chain has all functions available in the math namespace, and has a number of special functions:
    done()     Finalize the chain and return the chain’s value.
    valueOf()  The same as done(), returns the chain’s value.
    toString() Executes math.format(value) onto the chain’s value, returning a string representation of the value.
*/
consol(math.chain(0.1).add(0.2).done()) // 0.30000000000000004
consol(math.chain(0.1).add(0.2).valueOf()) // 0.30000000000000004
consol(math.chain(0.1).add(0.2).toString()) // 0.30000000000000004

consol(typeof math.chain(0.1).add(0.2).done()) // number
consol(typeof math.chain(0.1).add(0.2).valueOf()) // number
consol(typeof math.chain(0.1).add(0.2).toString()) // string

chain调用,返回的是Chain的实例对象,就需要尾部调用,返回chain的value值,如上。

/*
* Function format
* Format a value of any type into a string.
*/
consol(math.format(math.chain(0.1).add(0.2).done())) // 0.30000000000000004
consol(typeof math.format(math.chain(0.1).add(0.2).done())) // string

看上去,math.format()只是把number类型转成string类型,别慌,后面讲bignumber时,才会真正体会到它的价值。

BigNumbers

划重点:对于具有任意精度的计算,math.js支持BigNumber数据类型,bignumber返回一个Decimal类

/*
* BigNumbers
* 对于具有任意精度的计算,math.js支持BigNumber数据类型
* bignumber返回一个Decimal类
*/
const _a = math.bignumber(0.1) // Decimal {s: 1, e: -1, d: Array(1), constructor: ƒ}
const _b = math.add(math.bignumber(0.1), math.bignumber(0.2)) // Decimal {s: 1, e: -1, d: Array(1), constructor: ƒ}
math.format(_b) // 0.3

math.format(),返回了Decimal类的value值。

总结一下,math的链式调用与rxjs差不多,写法非常灵活,可根据实际场景组合使用。来一个简单的例子:

const addNumber = math.format(
  math.chain(math.bignumber(0.1))
    .add(math.bignumber(0.2))
    .done()
)

const a = math.format(
  math.add(math.bignumber(0.1), math.bignumber(0.2))
)

Subtract(减法运算)

consol(1 - 0.9) // 0.09999999999999998

const subtractNumber = math.format(
  math.chain(1)
    .subtract(math.bignumber(0.9))
    .done()
)
consol(subtractNumber) // 0.1
const subtractNumber_1 = math.format(
  math.subtract(math.bignumber(1), math.bignumber(0.9))
)
consol(subtractNumber_1) // 0.1

Multiply(乘法运算)

consol(11 * 1.2 * 3) // 39.599999999999994

const multiplyMumber = math.format(
  math.multiply(math.bignumber(11), math.bignumber(1.2), math.bignumber(3))
)
consol(multiplyMumber) // 39.6

Divide(除法运算)

consol(1.2 / 3) // 0.39999999999999997

const divideNumber = math.format(
  math.divide(math.bignumber(1.2), math.bignumber(3))
)
consol(divideNumber) // 0.4

const divideNumber_1 = math.format(
  math.chain(math.bignumber(1.2)).divide(math.bignumber(3)).done()
)
consol(divideNumber_1) // 0.4

简单总结一下,简单的加减乘除都可以按照上面的实现,要进行组合计算时,就可以使用链式调用.
还有一些场景需要处理,

保留N位小数的问题

Round(四舍五入)

// Round 四舍五入,保留2位小数
consol(math.round(12.0355, 3)) // 12.036
consol(math.round(12.0354, 3)) // 12.035
consol(math.round(12.0354, 2)) // 12.04
consol(math.round(12.100, 2)) // 12.1

最后,再来一个综合实例

consol(
  +math.format(
    math.round(
      math.chain(0.1).add(2, 1).subtract(0.1).multiply(1.2, 33).divide(10000).done(),
      3
    )
  )
)
// 0.011879999999999998 => 0.012

掌握到基础用法之后,我们来手动实现一两个方法

手动实现一个Round

实现非常简单,2行代码,没有任何意外的情况。

// toFixed 即使位数不够,也会用0补充
consol(Number(12.035).toFixed(2)) // 12.04
consol(Number(12.1).toFixed(2)) // 12.10
consol(Number(12).toFixed(2)) // 12.00
// string转number
function formatNumber(number, len = 2) {
  // 四舍五入
  number = Number(number).toFixed(len)
  // 去尾部的0
  return Number(number)
}
consol(formatNumber(12.0355, 3)) // 12.036
consol(formatNumber(12.0354, 3)) // 12.035
consol(formatNumber(12.0354, 2)) // 12.04
consol(formatNumber(12.100, 2)) // 12.1

手动实现一个Add

基本逻辑:把需要计算的数字升级(乘以10的n次幂)成计算机能够精度识别的整数,等计算完毕后在降级(除以10的n次幂)

function add(...numList) {
    const base = 10
    const maxPrecision = numList.reduce((curMaxPrecision, num) => {
        const curPrecision = getNumPrecision(num)
        return curMaxPrecision > curPrecision ? curMaxPrecision : curPrecision
    })
    const enlargeScale = Math.pow(base, maxPrecision)
    const startNum = 0
    const counts = numList.reduce((total, num) => {
        return total + num * enlargeScale
    }, startNum)
    return counts / enlargeScale
}

function getNumPrecision(num) {
    return (num.toString().split('.')[1] || '').length
}

consol(0.1 + 0.2 + 0.3)) // 0.6000000000000001
consol(add(0.1, 0.2, 0.3)) // 0.6

其他库解决方案
bignumber.js
big.js
decimal

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Vue中解决JavaScript计算精度问题,可以使用第三方库math.js。通过引入math.js库,我们可以避免在浮点数计算中出现的精度问题。 在Vue中使用math.js可以按照以下步骤进行: 1. 首先,安装math.js库。可以使用npm或者yarn进行安装,例如:`npm install mathjs`。 2. 在Vue组件中引入math.js库,可以使用import语句进行引入,例如:`import math from 'mathjs'`。 3. 在需要进行计算的地方,使用math.js提供的相应方法进行计算。例如,对于减法,可以使用`math.subtract()`方法,对于加法,可以使用`math.add()`方法,对于除法,可以使用`math.divide()`方法。 4. 由于math.js处理的是大整数和大浮点数,所以在进行计算之前,需要将需要计算的数字转换成math.js支持的格式。可以使用`math.bignumber()`方法将数字转换成math.js支持的格式。 5. 最后,使用`math.format()`方法将计算结果转换成字符串格式,以避免出现科学计数法或多余的小数位。 举例来说,如果要解决0.1 + 0.2的计算精度问题,可以按照以下代码进行操作: ```javascript import math from 'mathjs' const addNumber = math.format(math.add(math.bignumber(0.1), math.bignumber(0.2))) console.log(addNumber) // 输出0.3 ``` 使用类似的方法,可以解决减法和除法的计算精度问题。请记住,在进行计算之前,将需要计算的数字转换成math.js支持的格式,然后使用相应的数学方法进行计算,并最后使用`math.format()`方法转换结果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [vue使用mathjs解决前端计算精度不足问题](https://blog.csdn.net/qq_43555948/article/details/119885043)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值