浮点数精度问题:0.1 + 0.2

解释 JavaScript 浮点数精度丢失的原因及解决方案

问题

为什么 0.1 + 0.2 !== 0.3?如何解决?

console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false

解答

原因

JavaScript 使用 IEEE 754 双精度浮点数标准存储数字。十进制的 0.1 和 0.2 转换为二进制时是无限循环小数,存储时会被截断,导致精度丢失。

// 0.1 的二进制表示(无限循环)
// 0.0001100110011001100110011001100110011001100110011001101...

// 实际存储的值
console.log(0.1.toPrecision(20)); // "0.10000000000000000555"
console.log(0.2.toPrecision(20)); // "0.20000000000000001110"

解决方案

1. 使用 toFixed 转换

function add(a, b) {
  return parseFloat((a + b).toFixed(10));
}

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

2. 转为整数计算

function add(a, b) {
  // 获取小数位数
  const precision = Math.max(
    (a.toString().split('.')[1] || '').length,
    (b.toString().split('.')[1] || '').length
  );
  const multiplier = Math.pow(10, precision);
  
  // 转整数计算后再除回来
  return (Math.round(a * multiplier) + Math.round(b * multiplier)) / multiplier;
}

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

3. 使用 Number.EPSILON 比较

function isEqual(a, b) {
  // Number.EPSILON 是 JavaScript 最小精度值
  return Math.abs(a - b) < Number.EPSILON;
}

console.log(isEqual(0.1 + 0.2, 0.3)); // true

4. 使用第三方库

// 使用 decimal.js
import Decimal from 'decimal.js';

const result = new Decimal(0.1).plus(0.2);
console.log(result.toNumber()); // 0.3

// 使用 big.js
import Big from 'big.js';

const sum = new Big(0.1).plus(0.2);
console.log(sum.toNumber()); // 0.3

关键点

  • JavaScript 使用 IEEE 754 双精度浮点数,64 位存储(1 符号位 + 11 指数位 + 52 尾数位)
  • 0.1 和 0.2 转二进制是无限循环小数,存储时被截断导致精度丢失
  • 简单场景用 toFixed 或转整数计算
  • 精度要求高的场景(如金融计算)使用 decimal.js 或 big.js
  • 比较浮点数相等时使用 Number.EPSILON 作为误差范围