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 等。
解决方法
-
整数放大法(适合简单计算):
javascript// 将小数转为整数计算后再还原 const calculate = (a, b) => (a * 1000 + b * 1000) / 1000; console.log(calculate(0.1, 0.2)); // 0.3
-
高精度库(推荐):
javascript// 使用 decimal.js import { Decimal } from 'decimal.js'; const result = new Decimal(0.1).plus(0.2).toNumber(); console.log(result); // 0.3
-
内置精度处理(ES6):
javascript// 使用 Number.EPSILON 比较 console.log(Math.abs(0.3 - (0.1 + 0.2)) < Number.EPSILON); // true
⚠️ 在财务等敏感场景中,必须使用高精度库 (如
decimal.js
或big.js
),整数放大法在超出安全范围时仍会出错。