谈谈 js 小数加法精度问题

一、问题现象

我们从一个最常见的例子说起:

js 复制代码
console.log(0.1 + 0.2); 

这行代码:0.1 加 0.2,应该是等于0.3的,但实际上并不是

js 复制代码
console.log(0.1 + 0.7 === 0.8); 

这行代码:0.1 加 0.7,应该是等于0.8的,打印应该为true,实际并不是

这种微小的偏差看似没什么大问题,但如果你用它来作条件判断

js 复制代码
console.log(0.1 + 0.7 === 0.8); // 输出:false

这就可能直接导致业务逻辑错误,是个严重的bug。

二、为什么会出现精度丢失

这个问题的根源在于计算机内部使用二进制来表示数字,而许多十进制小数无法在二进制中精确表示。

在二进制中,0.1 实际上是一个无限循环的小数,就像十进制中 1/3 = 0.333... 一样。IEEE 754 浮点数标准中,只能保留有限的位数,因此这种无限循环的二进制小数会被截断,从而造成精度损失。

当我们写下 0.1 时,计算机实际存储的是最接近 0.1 的二进制表示,这个值略微不等于真正的 0.1。就像我们无法用十进制精确表示 1/3 一样,计算机也无法用二进制精确表示某些十进制小数。

简单来说:

  • 十进制的 0.1 在二进制中表示为:0.00011001100110011...(循环)
  • 二进制截断后转换为十进制,就是我们看到的 0.10000000000000000555...

多个这种带误差的数相加,自然就出现了结果偏差。

三、常见解决方案及代码示例

1. 使用 toFixed 或 toPrecision 限制精度

js 复制代码
let result = (0.1 + 0.2).toFixed(2); // '0.30' 字符串
console.log(result); // 输出:'0.30'
console.log(Number(result)); // 输出:0.3

优点: 简单直观
缺点: 返回的是字符串,需手动转为数字;四舍五入可能带来新的误差


2. 放大整数再计算,再缩小回去

这是金融类系统中常用的思路。比如先将 0.1 和 0.2 变为 10 和 20,相加后再除以 10。

js 复制代码
function add(a, b) {
  let base = Math.pow(10, Math.max(decimalLength(a), decimalLength(b)));
  return (a * base + b * base) / base;
}

function decimalLength(num) {
  const parts = num.toString().split('.');
  return parts[1] ? parts[1].length : 0;
}

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

优点: 精度高,适用于加减法
缺点: 实现稍复杂,乘除法需额外处理


3. 使用第三方库(推荐)

对于需要高精度计算的场景,建议使用成熟库来处理,比如:

✅ decimal.js(体积更大 几十KB)
js 复制代码
npm install decimal.js
js 复制代码
import Decimal from 'decimal.js';

const result = new Decimal(0.1).plus(0.2);
console.log(result.toNumber()); // 输出:0.3
✅ big.js 体积更小 几KB)
js 复制代码
npm install big.js
js 复制代码
import Big from 'big.js';

const result = new Big(0.1).plus(0.2);
console.log(result.toNumber()); // 输出:0.3
两者对比
特性 decimal.js big.js
精度控制 支持任意精度,功能更丰富 精度控制简单,但够用
API 接口 .plus(), .minus(), .times()等 同样使用 .plus()等一致方法名
文件体积 较大(几十 KB) 更轻量(几 KB)
功能 支持三角函数、开方、对数等复杂运算 更专注于基本加减乘除
精度表现 非常高 同样可靠

如果只是简单加减,没有复杂的运算,推荐big.js,因为体积更小。

总结

js 中小数相加产生精度丢失,并非语言缺陷,而是由于底层 IEEE 754 双精度浮点数的表示机制所致。像 0.1 + 0.2 !== 0.3 的问题,在日常开发中常常被忽视,却可能在关键业务场景中引起问题,从而出现bug,所以在日常开发中掌握其解决办法,可以提前避免相关问题。

相关推荐
拆房老料42 分钟前
ONLYOFFICE也能像Microsoft Word和WPS一样分别设置中西文字体
前端·html·word·开源软件·wps
Solis程序员1 小时前
Stripe 幂等表:把「不确定的重试」变成「查表返回」
前端·安全·bootstrap·php·agent
李纲明1 小时前
WordPress 站点变慢:按 TTFB → 缓存 → 查询 → 前端 分层排查(含配置注意)
前端·缓存·性能优化·wordpress·后端开发
Zelman1 小时前
测试层级与测试类型
后端·面试·测试
程序人生8881 小时前
PDF 转 Word 版式错乱、表格丢失?DocConverter Web 格式互转引擎的保真实践
前端·人工智能·opencv·机器学习·pdf·word
考研保研资料分享2 小时前
自动化控制保研经验合辑:上交夏令营、北工大预推免与跨专业面试复盘(2025)
运维·面试·自动化
粥里有勺糖2 小时前
安利一下最近用的桌面“Agent” | T3 Code
前端·github·ai编程
xy34532 小时前
Axure 9.0 动态面板基本操作
前端·ui·html·axure·原型·产品设计
不停喝水3 小时前
【前端转全栈java速通课】 项目实战④7-11节 操作数据库-登录-注册-修改密码-注销-mubatis-plus简化crud-
java·前端·数据库
一水鉴天3 小时前
统一对象拓扑(UOT)设计全链定稿:从三件事分离到三继承 之1 20261010(豆包“查看聊天内容”继续)
前端·css