当前位置: 首页 > news >正文

JavaScript 金额运算精度丢失问题及解决方案

JavaScript 金额运算精度丢失问题及解决方案

    • 1. 前言
    • 2. 为什么 JavaScript 计算金额会精度丢失?
      • 2.1 JavaScript 使用 IEEE 754 双精度浮点数
      • 2.2 浮点运算错误示例
        • **错误示例 1:0.1 + 0.2 ≠ 0.3**
        • **错误示例 2:浮点乘法精度问题**
    • 3. 解决方案
      • **方案 1:使用整数运算(推荐)**
      • **方案 2:使用 `toFixed()`(简单但不推荐)**
      • **方案 3:使用 `Number.EPSILON` 进行误差修正**
      • **方案 4:使用 BigDecimal 类库(最佳方案)**
      • **方案 5:使用 ES11 `BigInt`(适用于整数金额计算)**
    • 4. 结论
    • 5. 总结

1. 前言

在 JavaScript 中,浮点数运算可能会产生精度丢失的问题,尤其在处理 金额计算 时,这可能会导致严重的业务逻辑错误。例如:

console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.07 * 100); // 7.000000000000001
console.log(0.1 + 0.7 === 0.8); // false

这些问题主要是由于 JavaScript 使用 IEEE 754 双精度浮点数(64 位)来表示数字,某些小数无法用二进制精确表示,从而导致精度丢失。

本篇文章将深入剖析 JavaScript 金额计算精度丢失的原因,并提供多种 解决方案 来避免这些问题。


2. 为什么 JavaScript 计算金额会精度丢失?

2.1 JavaScript 使用 IEEE 754 双精度浮点数

JavaScript 所有的 Number 类型 都遵循 IEEE 754 双精度浮点数(64 位)标准,它的存储方式如下:

符号位指数位尾数位(小数部分)
1 位11 位52 位

某些十进制小数无法用二进制精确表示,类似于 1/3 在十进制中是无限循环小数 0.3333...,而 0.1二进制 下也会变成 无限循环小数

0.1(十进制) = 0.000110011001100110011...(二进制 无限循环)

由于存储空间限制,JavaScript 只能截取前 52 位,导致数值被四舍五入,最终引发计算误差。


2.2 浮点运算错误示例

错误示例 1:0.1 + 0.2 ≠ 0.3
console.log(0.1 + 0.2); // 0.30000000000000004

解析

  1. 0.1 的二进制近似值:0.00011001100110011...
  2. 0.2 的二进制近似值:0.0011001100110011...
  3. 计算后,得到的二进制不是 精确的 0.3,而是 0.30000000000000004

错误示例 2:浮点乘法精度问题
console.log(0.07 * 100); // 7.000000000000001

0.07 转换成二进制后是 0.000100011110101110000101...,乘以 100 后的结果并不完全等于 7


3. 解决方案

方案 1:使用整数运算(推荐)

核心思想
避免小数计算,将小数转换为整数计算,计算完成后再转回小数。

function add(a, b) {
    return (a * 100 + b * 100) / 100;
}

console.log(add(0.1, 0.2)); // 0.3
console.log(add(0.07, 0.02)); // 0.09

适用场景

  • 适用于 加法、减法、乘法、除法 计算。
  • 适用于 金额计算,例如:分(整数)代替元(小数)。

方案 2:使用 toFixed()(简单但不推荐)

console.log((0.1 + 0.2).toFixed(2)); // "0.30"
console.log((0.07 * 100).toFixed(2)); // "7.00"

缺点

  • toFixed() 返回的是 字符串,如果要继续计算,还需转换回 Number 类型:
    let result = Number((0.1 + 0.2).toFixed(2)); // 0.3
    
  • 不能完全解决浮点运算的问题,只适用于 结果展示

方案 3:使用 Number.EPSILON 进行误差修正

原理
Number.EPSILON 是 JavaScript 最小的精度差值(约 2.22e-16),可用于修正浮点计算误差:

function add(a, b) {
    return Math.round((a + b + Number.EPSILON) * 100) / 100;
}

console.log(add(0.1, 0.2)); // 0.3
console.log(add(0.07, 0.02)); // 0.09

适用场景

  • 用于 浮点数精度修正,避免直接使用 toFixed()

方案 4:使用 BigDecimal 类库(最佳方案)

JavaScript 原生 Number 无法解决所有精度问题,因此可以使用 BigDecimal 第三方库 来进行精准计算,例如 decimal.js

安装:

npm install decimal.js

使用:

const Decimal = require('decimal.js');

console.log(new Decimal(0.1).plus(0.2).toNumber()); // 0.3
console.log(new Decimal(0.07).times(100).toNumber()); // 7

优点

  • 可以处理 任意精度 运算,特别适用于 金融计算

方案 5:使用 ES11 BigInt(适用于整数金额计算)

BigInt 可用于 大数计算,但 不支持小数

const a = BigInt(100); // 1.00 元
const b = BigInt(200); // 2.00 元

console.log((a + b) / BigInt(100)); // 3n (表示 3 元)

适用场景

  • 仅适用于 整数计算(如分单位),不适用于 小数计算

4. 结论

方案适用场景优点缺点
整数运算(推荐)金额计算高效、适用范围广需要手动转换
toFixed()仅限展示简单易用结果为字符串,无法继续计算
Number.EPSILON浮点修正适用于加减运算不适用于复杂运算
decimal.js(最佳)任何精度计算高精度,API 强大需引入库
BigInt整数计算适用于大数运算不支持小数

5. 总结

JavaScript 的浮点运算容易导致金额计算误差,我们可以通过 整数运算、Number.EPSILON、BigDecimal 库等方式 来解决。

如果你正在开发电商、金融、结算系统推荐使用 decimal.js 或整数运算,以保证计算精度。

你还遇到过 JavaScript 金额计算的问题吗?欢迎留言讨论!🚀

相关文章:

  • 【LLMs篇】05:RMSNorm
  • AI 大模型统一集成|微服务 + 认证中心:如何保障大模型 API 的安全调用!
  • docker 部署elk 设置账号密码
  • MAT 启动报错
  • MySQL数据库精研之旅第一期:开启数据管理新旅程
  • yum软件包乾坤大挪移(Yum Package Qiankun Great Migration)
  • mysql 数据库异常排查
  • 认知篇#4:YOLO评价指标及其数学原理的学习
  • 单表查询和多表查询
  • 时序分析笔记
  • PyCharm 5的Python IDE的功能(附工具下载)
  • 建筑兔零基础自学记录49|python爬取百度地图POI实战-3
  • 康谋方案 | AVM合成数据仿真验证方案
  • 优选算法系列(2.滑动窗口_下)
  • Java+Html实现前后端客服聊天
  • anythingLLM之stream-chat传参
  • HttpServletRequest 获取 JSESSIONID
  • 3、数据库的事务隔离级别有哪些?【高频】
  • AOP切入点表达式
  • acwing1233.全球变暖
  • 巴基斯坦外长:印巴已同意立即停火
  • 构筑高地共伴成长,第六届上海创新创业青年50人论坛在沪举行
  • 昆明一学校门外小吃摊占满人行道,城管:会在重点时段加强巡查处置
  • 美众议院通过法案将“墨西哥湾”更名为“美国湾”
  • 汪明荃,今生不负你
  • 美联储如期按兵不动,强调“失业率和通胀上升的风险均已上升”(声明全文)