JavaScript 浮点数计算精度错误示例

JavaScript 浮点数计算精度错误示例

在 JavaScript 中,浮点数精度问题源于其使用 IEEE 754 双精度浮点数标准(64位二进制表示)。这种表示法无法精确表示某些十进制小数,导致计算出现微小误差。以下是典型示例:

示例 1:基础加法错误
javascript 复制代码
// 预期:0.1 + 0.2 = 0.3
console.log(0.1 + 0.2 === 0.3);  // 输出:false
console.log(0.1 + 0.2);           // 输出:0.30000000000000004

原因分析
0.10.10.1 和 0.20.20.2 在二进制中是无限循环小数:

  • 0.110=0.0001100110011...20.1_{10} = 0.0001100110011..._20.110=0.0001100110011...2
  • 0.210=0.001100110011...20.2_{10} = 0.001100110011..._20.210=0.001100110011...2
    计算时发生二进制舍入误差。

示例 2:财务计算错误
javascript 复制代码
// 计算含税价(税率13%)
const price = 100.01;
const taxRate = 0.13;

// 预期:不含税价 = 100.01 / 1.13 ≈ 88.50442477876106
const result = price / (1 + taxRate);
console.log(result);  // 输出:88.50442477876106(看似正确)

// 但验证时出现偏差:
const verify = result * 1.13;
console.log(verify);  // 输出:100.01000000000001(多出0.00000000000001)

问题本质

浮点数的二进制表示无法精确等价于十进制 0.130.130.13,导致累积误差。


示例 3:乘法精度丢失
javascript 复制代码
// 计算商品总价(单价8.85元 × 数量3)
const total = 8.85 * 3;
console.log(total); // 输出:26.549999999999997(而非26.55)

关键点
8.858.858.85 在二进制中是 1000.1100110011...21000.1100110011..._21000.1100110011...2(无限循环),乘法放大舍入误差。


示例 4:比较操作失效
javascript 复制代码
const a = 0.3 - 0.2;  // 0.09999999999999998
const b = 0.2 - 0.1;  // 0.1

// 预期:a 和 b 应相等(0.3-0.2=0.2-0.1)
console.log(a === b);  // 输出:false

解决方案

使用误差范围比较:

javascript 复制代码
function isEqual(x, y, epsilon = 1e-10) {
  return Math.abs(x - y) < epsilon;
}
console.log(isEqual(a, b));  // 输出:true

精度问题本质

JavaScript 的数值范围限制:

  • 安全整数范围 :[−253,253][-2^{53}, 2^{53}][−253,253](即 [−9007199254740991,9007199254740991][-9007199254740991, 9007199254740991][−9007199254740991,9007199254740991])
  • 浮点数精度 :仅能精确表示分母为 2n2^n2n 的小数(如 0.5,0.250.5, 0.250.5,0.25),无法精确表示 0.1,0.20.1, 0.20.1,0.2 等。

解决方法

  1. 整数放大法(适合简单计算):

    javascript 复制代码
    // 将小数转为整数计算后再还原
    const calculate = (a, b) => (a * 1000 + b * 1000) / 1000;
    console.log(calculate(0.1, 0.2)); // 0.3
  2. 高精度库(推荐):

    javascript 复制代码
    // 使用 decimal.js
    import { Decimal } from 'decimal.js';
    const result = new Decimal(0.1).plus(0.2).toNumber();
    console.log(result); // 0.3
  3. 内置精度处理(ES6):

    javascript 复制代码
    // 使用 Number.EPSILON 比较
    console.log(Math.abs(0.3 - (0.1 + 0.2)) < Number.EPSILON); // true

⚠️ 在财务等敏感场景中,必须使用高精度库 (如 decimal.jsbig.js),整数放大法在超出安全范围时仍会出错。

相关推荐
别来无恙1494 分钟前
Java Web开发:Session与Cookie详细入门指南
java·开发语言
华阙之梦21 分钟前
QT环境搭建
开发语言·qt
GalaxyPokemon44 分钟前
Linux的pthread怎么实现的?(包括到汇编层的实现)
运维·开发语言·c++
凉_橙1 小时前
什么是抽象语法树?
前端·javascript
页面魔术1 小时前
尤雨溪: 我们没有放弃虚拟 dom
前端·javascript·vue.js
lsx2024061 小时前
Ruby 条件判断
开发语言
臻实1 小时前
Win10系统Ruby+Devkit3.4.5-1安装
开发语言·后端·ruby
IT毕设实战小研1 小时前
Java毕业设计选题推荐 |基于SpringBoot的水产养殖管理系统 智能水产养殖监测系统 水产养殖小程序
java·开发语言·vue.js·spring boot·毕业设计·课程设计
Hilaku1 小时前
深入理解npm、pnpm和yarn的lock文件,我发现了一些细节
前端·javascript·npm
Gu_shiwww2 小时前
数据结构3线性表——单链表(C)
c语言·开发语言·数据结构