🔢前端解决浮点数运算精度丢失的问题

场景复现

js 复制代码
const value1 = 15966.2 + 3103.3 + 1562.9 + 933.9
console.log('👉 ~ value1:', value1)

上面的这些数值相加的结果应该是21566.3

但是在浏览器中运算的时候,结果却变成了21566.300000000003

那么为什么会这样呢?

这是因为 JavaScript 内部的数字均以 IEEE 754 标准的双精度浮点数格式存储,这种格式只能精确表示有限个小数,而对于一些无限循环小数或无理数,无法精确表示,就会出现精度丢失的情况。

有一些前端的面试题就会问你 为什么0.1+0.2的结果不等于0.3

总之, 前端计算存在这么一个问题。

几种解决方案对比

转化为整数计算

js 复制代码
function addDecimals(num1, num2) {
    const multiplier = Math.pow(
      10,
      Math.max(
        num1.toString().split('.')[1]?.length || 0,
        num2.toString().split('.')[1]?.length || 0
      )
    )
    return (
      (Math.round(num1 * multiplier) + Math.round(num2 * multiplier)) /
      multiplier
    )
}
console.log(addDecimals(0.1, 0.2)) // 0.3
console.log(addDecimals(0.15, 0.15)) // 0.3

这种方只适合小数量计算,大数还是不行

用toFixed()格式化

这种方式没有什么意义,只能展示的时候用,并且也没有解决这个问题

位数放大之后会发现还是有问题

字符串模拟运算

这种方式是目前最为靠谱的一种方式,从源头上解决了这个问题。

很多处理浮点数相关的第三方库实现原理都是基于这种方式来实现的

第三方库

  • Math.js:一个用于 JavaScript 和 Node.js 的扩展数学库

  • decimal.js: 计算任意精度的十进制类型。

  • big.js:一个小型,快速,易于使用的库,用于任意精度的十进制算术运算。

  • bignumber.js: 一个用于任意精度算术的 JavaScript 库。

这些第三方库都可以解决这个问题

bignumber.js的使用

这边我以这个库为例来解决这个问题,这个库的解决原理就是前面提到的字符串模拟运算。

安装

用包管理器安装

shell 复制代码
npm install bignumber.js

或者直接下载bignumber.js文件,通过script引入

github地址:MikeMcl/bignumber.js: 一个用于任意精度小数和非小数运算的 JavaScript 库 --- MikeMcl/bignumber.js: A JavaScript library for arbitrary-precision decimal and non-decimal arithmetic

cdn地址:cdn.jsdelivr.net/npm/bignumb...

相加

js 复制代码
let sum = new BigNumber(0)
const numbers = [15966.2, 3103.3, 1562.9, 933.9]
numbers.forEach((value) => (sum = sum.plus(value)))
console.log('👉 ~ numbers:', sum.toString())

结果如下:

相减

js 复制代码
const numbers = [15966.2, 3103.3, 1562.9, 933.9]
const value1 = new BigNumber(15966.2)
console.log('👉 ~ value1:', value1.minus(1.1231).toString())

结果如下:

相乘

js 复制代码
const value1 = new BigNumber(15966.2)
console.log('👉 ~ value1:', value1.multipliedBy(21).toString())

结果如下:

相除

js 复制代码
const value1 = new BigNumber(15966.2)
console.log('👉 ~ value1:', value1.div(21).toString())

结果如下:

判断是否相等和比较大小

js 复制代码
const value1 = new BigNumber(15966.2)
const value2 = new BigNumber(15966.3)
const value3 = new BigNumber(15966.2)
const value4 = new BigNumber(15966.1)
console.log('15966.2 -> 15966.3 ', value1.comparedTo(value2))
console.log('15966.2 -> 15966.2 ', value1.comparedTo(value3))
console.log('15966.2 -> 15966.1 ', value1.comparedTo(value4))

结果如下:

  • -1: 小于
  • 0: 相等
  • 1: 大于

结尾

像这种涉及到精度计算的,开发中最好都是直接用第三方库去处理,避免出现计算错误之类的问题,导致背大锅。

相关推荐
学网安的肆伍31 分钟前
【046-WEB攻防篇】注入工具&SQLMAP&Tamper编写&指纹修改&高权限操作&目录架构
前端·架构
森林的尽头是阳光1 小时前
tree回显问题
javascript·vue.js·elementui
weixin_BYSJ19871 小时前
「课设设计」springboot校园超市助购系统26449 (附源码)
java·javascript·spring boot·python·django·flask·php
Highcharts.js2 小时前
如何下载使用Highcharts Grid 开发web可编辑表格
前端·人工智能·grid 表格·web 表格·在线编辑表格·highcharts表格安装·表格开发工具
用户62960593247052 小时前
从“浏览器根本区分不了”到真正实现:刷新不退出,关闭标签页自动退出,前端判断浏览器关闭和刷新
前端
qetfw2 小时前
MWU:Vue 3 + FastAPI 的 MaaFramework 跨平台 WebUI 源码
前端·vue.js·python·fastapi·开源项目·效率工具
mfxcyh2 小时前
Vue3+Ant Design Vue 实现手动异步文件上传(含大小校验、弹窗上传)
前端·javascript·vue.js
wordbaby2 小时前
Web端热敏标签打印完整解决方案(QZ Tray + 译维A42)
前端
swipe2 小时前
03|Axios 请求进了后端之后:Controller、Request、Response 是怎么接住它的?
前端·后端·全栈
meilindehuzi_a2 小时前
Vue3进阶基础:指令修饰符、v-model表单绑定、动态样式、计算属性与侦听器实战
前端·javascript·vue.js