当前位置: 首页 > 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 金额计算的问题吗?欢迎留言讨论!🚀


http://www.mrgr.cn/news/95069.html

相关文章:

  • Can通信流程
  • vector容器以及deque
  • 指令系统1(数据传输指令)
  • java面试题,什么是动态代理?、动态代理和静态代理有什么区别?说一下反射机制?JDK Proxy 和 CGLib 有什么区别?动态代理的底层
  • Windows 图形显示驱动开发-WDDM 3.0功能- 硬件翻转队列(五)
  • 876.链表的中间节点
  • 图莫斯TOOMOSS上位机TCANLINPro使用CAN UDS功能时 编写、加载27服务dll解锁算法文件
  • 霍尔传感器与电流互感器的区别
  • 别让时光溜走!Kairos App 帮你抓住每一刻
  • SpringBoot3+Vue3实战(Vue3快速开发登录注册页面并对接后端接口)(4)
  • Lombok常用注解
  • 男女搭配(数学思维)
  • YOLO魔改之频率分割模块(FDM)
  • stm32第七天震动传感器
  • 【模拟】从 0 到 1:模拟算法的深度剖析与实战指南
  • python实现接口自动化
  • 【MySQL数据库】多表查询(笛卡尔积现象,联合查询、内连接、左外连接、右外连接、子查询)-通过练习快速掌握法
  • 4.好事多磨 1
  • C++项目:高并发内存池_上
  • 【深度学习与大模型基础】第6章-对角矩阵,对称矩阵,正交矩阵