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

相关推荐
小着2 小时前
vue项目页面最底部出现乱码
前端·javascript·vue.js·前端框架
lichenyang4534 小时前
React ajax中的跨域以及代理服务器
前端·react.js·ajax
呆呆的小草4 小时前
Cesium距离测量、角度测量、面积测量
开发语言·前端·javascript
WHOAMI_老猫5 小时前
xss注入遇到转义,html编码绕过了解一哈
javascript·web安全·渗透测试·xss·漏洞原理
一 乐5 小时前
民宿|基于java的民宿推荐系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·源码
sunny-ll6 小时前
【C++】详解vector二维数组的全部操作(超细图例解析!!!)
c语言·开发语言·c++·算法·面试
testleaf6 小时前
前端面经整理【1】
前端·面试
好了来看下一题6 小时前
使用 React+Vite+Electron 搭建桌面应用
前端·react.js·electron
啃火龙果的兔子6 小时前
前端八股文-react篇
前端·react.js·前端框架
小前端大牛马6 小时前
react中hook和高阶组件的选型
前端·javascript·vue.js