谈谈 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,所以在日常开发中掌握其解决办法,可以提前避免相关问题。

相关推荐
Da Da 泓几秒前
五子棋 Web 端测试用例报告
前端·网络·websocket·网络协议·测试用例·项目
zxcvb1531 小时前
远程查看本地文档:一条轻量 Web 文件服务的工程化链路
前端·远程查看文档
niucloud-admin1 小时前
NIUCLOUD PHP SAAS上门家政使用文档:城市管理
java·前端·php
程序员大阳1 小时前
副队长JavaScript教程(50)-DOM:网页开发的起点
javascript·dom·es·节点·概念·document·关系
北岛贰1 小时前
啵啵音乐迎来全新升级!
前端
hkNaruto3 小时前
【AI】基于大模型的AI游戏地图生成、控制方案:AI Native Game Harness 迁移操作手册
前端·人工智能·游戏
IT_陈寒3 小时前
Java序列化踩坑记:对象循环引用让我加班两小时
前端·人工智能·后端
每天进步一点点km3 小时前
自用-牛客网-华为机试编程模拟题1-python
华为od·华为·面试
Acro4 小时前
快给macOS 上的Grok Bot换一套皮肤
前端
flash俊杰4 小时前
双引擎 OCR 编排:逐页判定、视觉优先、partial 语义与三层超时
前端