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),整数放大法在超出安全范围时仍会出错。

相关推荐
酷酷的身影3 小时前
Drivers/LedManager.cs
开发语言·php
小樱花的樱花3 小时前
Linux 线程的创建
linux·c语言·开发语言
xiaoshuaishuai83 小时前
C# AI实现PR处理、单元测试
开发语言·c#·log4j
C++、Java和Python的菜鸟3 小时前
第7章 Java高级技术
java·开发语言
LONGZHIQIN3 小时前
C#基础复习笔记
开发语言·笔记·c#
可靠的仙人掌3 小时前
SAC(Soft Actor-Critic)算法底座
开发语言·算法·php
学逆向的4 小时前
汇编——数据存储模式
开发语言·汇编·网络安全
geovindu4 小时前
CSharp: Prototype Pattern
开发语言·后端·设计模式·.net·原型模式·创建型模式
天天进步20155 小时前
Python全栈项目--校园食堂点餐与推荐系统
开发语言·python
aaPIXa6225 小时前
C++模板元编程:编译期计算Fibonacci数列
java·开发语言·c++