0.1 + 0.2 不等于 0.3

在前端(JavaScript)里 0.1 + 0.2 ≠ 0.3,核心原因是 十进制小数在二进制中无法被精准表示,导致运算时出现精度丢失------这不是JS的bug,而是所有使用「二进制浮点数」的编程语言(如Python、Java)都存在的通用问题。

一、核心原理:十进制→二进制的精度丢失

我们使用用十进制 (0-9),但计算机底层只能用二进制 (0和1)。像 0.1、0.2 这样的十进制小数,转换成二进制后是无限循环小数 ,就像十进制中 1/3 = 0.333333... 无法写尽一样。

1. 具体转换过程(简化版)
  • 0.1 的二进制:0.0001100110011...(0011 无限循环)

  • 0.2 的二进制:0.001100110011...(0011 无限循环)

但JS使用的是 64位 双精度浮点数 标准(IEEE 754),只能存储有限位数(约16位有效数字),所以会对无限循环的二进制数做「舍入截断」,最终存储的 0.1 和 0.2 都是近似值,而非精确值:

  • 实际存储的 0.1 ≈ 0.10000000000000000555

  • 实际存储的 0.2 ≈ 0.20000000000000001110

2. 相加后的结果

这两个近似值相加:

0.10000000000000000555 + 0.20000000000000001110 = 0.30000000000000001665

所以我们看到的 0.1 + 0.2 结果是 0.30000000000000004(不同环境舍入略有差异),而非精确的 0.3。

二、代码验证:直观看到精度问题

javascript 复制代码
// 验证存储的近似值
console.log(0.1); // 输出 0.1(浏览器会美化显示,但实际是近似值)
console.log(0.1.toPrecision(20)); // 输出 0.10000000000000000555(真实存储值)
console.log(0.2.toPrecision(20)); // 输出 0.20000000000000001110

// 核心运算
console.log(0.1 + 0.2); // 输出 0.30000000000000004(≠ 0.3)
console.log(0.1 + 0.2 === 0.3); // 输出 false(重点!判断相等会出错)

三、前端开发中如何解决这个问题?

核心思路是 避免直接比较浮点数,或把小数转为整数运算后再转回小数,推荐3种常用方法:

方法1:固定小数位数(toFixed,适合简单场景)
javascript 复制代码
// toFixed(n) 保留n位小数(返回字符串,需转数字)
const result = (0.1 + 0.2).toFixed(1); 
console.log(result); // 输出 "0.3"
console.log(Number(result) === 0.3); // 输出 true

// 注意:toFixed会四舍五入,适合金额、简单计算
console.log((0.15).toFixed(1)); // 输出 "0.2"(四舍五入)
方法2:转整数运算(精准,适合金额/常用计算)

把小数乘以10的n次方转为整数(消除小数),运算后再除以10的n次方:

javascript 复制代码
// 0.1+0.2 → 转成 1+2=3 → 再÷10=0.3
function addFloat(a, b) {
  // 取最大小数位数(0.1是1位,0.2是1位,max=1)
  const decimalLen = Math.max(
    a.toString().split(".")[1]?.length || 0,
    b.toString().split(".")[1]?.length || 0
  );
  const multiplier = 10 ** decimalLen; // 10^1=10
  // 转整数运算,避免精度丢失
  return (a * multiplier + b * multiplier) / multiplier;
}

console.log(addFloat(0.1, 0.2)); // 输出 0.3
console.log(addFloat(0.15, 0.25)); // 输出 0.4
方法3:使用专业的精度库(适合复杂计算/金融场景)

如果涉及大量浮点数运算(如电商金额、财务系统),推荐用成熟库(如 decimal.js、big.js):

bash 复制代码
# 安装
npm install decimal.js
javascript 复制代码
import Decimal from 'decimal.js';

const a = new Decimal(0.1);
const b = new Decimal(0.2);
const result = a.plus(b).toNumber(); // plus是decimal.js的加法方法

console.log(result); // 输出 0.3
console.log(result === 0.3); // 输出 true

总结

  1. 0.1 + 0.2 ≠ 0.3 的本质是 十进制小数转二进制时的无限循环,导致浮点数存储为近似值,相加后精度丢失;

  2. 这是IEEE 754二进制浮点数的通用特性,并非JavaScript的bug;

  3. 前端解决思路:简单场景用 toFixed,通用场景转整数运算,复杂/金融场景用 decimal.js 等专业库,永远不要直接用 === 判断浮点数是否相等。

相关推荐
去伪存真8 小时前
并发量一高就装死?Agent 全链路“防猝死”手册
前端·agent
Moment8 小时前
AI 编程时代,聊聊我的 Mac mini 双屏桌面
前端·后端
AiMan8 小时前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
时间的拾荒人9 小时前
Redis 入门详解:核心特性、重要文件与全局命令实战
开发语言·redis·面试
总要学点什么9 小时前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模
上海懒图科技9 小时前
工厂厂区电子地图定位导航:厂区人车精准定位,安全合规双保障
前端·arcgis·vue
西安小哥9 小时前
端侧跑大模型,真正的门槛不在参数量:一次 596 秒到 10 秒的压缩路径复盘
前端·人工智能
陈随易10 小时前
Cloudflare 出钱,Vite 团队挑人,44 个维护者分 30 万美元
前端·后端·程序员
恋猫de小郭10 小时前
Flutter THLC 的 WebView 滚动终于从 70 FPS 提升到 120 FPS
android·前端·flutter
严肃小白兔11 小时前
TanStack Router 更新构建工具链:Node.js 兼容性、路由分割与前端工程实践
前端·node.js·状态模式