JavaScript 中精度问题以及解决方案

JavaScript 中的精度问题源于其遵循 IEEE 754 双精度浮点数标准,导致某些小数无法精确表示(如 0.1 + 0.2 !== 0.3)。以下是问题分析及解决方案:


问题原因

  1. 二进制浮点数的固有缺陷
    十进制小数(如 0.1)在二进制中是无限循环的,存储时会被截断,引发精度丢失。

  2. 安全整数范围限制
    JavaScript 的 Number 类型仅在 [-2^53 +1, 2^53 -1] 范围内能精确表示整数。


解决方案

1. 整数运算(避免浮点数)

将小数转换为整数(如“分”代替“元”),计算后再转回小数。注意数值范围,超出安全整数时使用 BigInt

// 示例:金额转为分计算
function toCents(value) {
  return Math.round(value * 100);
}
const total = (toCents(0.1) + toCents(0.2)) / 100; // 0.3

// 大整数使用 BigInt
const a = BigInt(Math.round(0.1 * 100)); // 10n
const b = BigInt(Math.round(0.2 * 100)); // 20n
const sum = Number(a + b) / 100; // 0.3

2. 使用高精度计算库

  • decimal.js:精确十进制浮点运算。
  • big.js:轻量库,适合货币计算。
// 使用 decimal.js
import Decimal from "decimal.js";
const sum = new Decimal(0.1).plus(0.2).toString(); // "0.3"

3. 控制显示精度(toFixedtoPrecision

用于最终展示,避免中间计算使用(可能导致误差累积)。

console.log((0.1 + 0.2).toFixed(2)); // "0.30"(字符串)

4. 比较时容忍误差范围

使用 Number.EPSILON 或自定义阈值。

// 默认容忍误差
function areEqual(a, b, epsilon = 1e-10) {
  return Math.abs(a - b) < epsilon;
}

// 动态阈值(针对不同数量级)
function dynamicAreEqual(a, b) {
  return Math.abs(a - b) < Number.EPSILON * Math.max(Math.abs(a), Math.abs(b));
}

5. 自定义高精度计算函数

处理加减乘除,通过放大为整数计算。

// 加法示例
function add(num1, num2) {
  const factor = 10 ** Math.max(getDecimals(num1), getDecimals(num2));
  return (num1 * factor + num2 * factor) / factor;
}

// 获取小数位数
function getDecimals(num) {
  const str = num.toString();
  return (str.split('.')[1] || '').length;
}

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

总结

场景 推荐方案
简单计算且范围可控 整数运算或放大为整数计算
复杂金融计算 使用 decimal.js 或 big.js
结果展示 toFixedtoPrecision
比较浮点数 误差范围判断

根据实际需求选择方案,复杂场景推荐优先使用成熟的第三方库。

posted @ 2025-04-15 17:07  张浩伟  阅读(457)  评论(0)    收藏  举报