0.1加0.2为什么不等于0.3-答不上来的都挂了

0.1 + 0.2 为什么不等于 0.3?答不上来的都挂了

这个问题你可能在面试、线上 Bug、甚至随手写 Demo 的时候都见过:

javascript 复制代码
console.log(0.1 + 0.2 === 0.3); // false

很多人第一反应是"浮点数精度问题",但如果继续追问:

  • 为什么偏偏是 0.10.2 这种小数出问题?
  • "精度"到底精在哪一位、丢在哪一步?
  • 实际开发里应该怎么比较、怎么计算才稳?

这篇文章按"现象 → 原因 → 解决 → 面试回答"的顺序,把它讲透。

先看现象

javascript 复制代码
console.log(0.1 + 0.2);  // 0.30000000000000004
console.log(0.1 + 0.7);  // 0.7999999999999999
console.log(0.3 - 0.2);  // 0.09999999999999998
console.log(0.1 * 3);    // 0.30000000000000004

但有些计算又是对的:

javascript 复制代码
console.log(0.5 + 0.5);    // 1
console.log(0.25 + 0.25);  // 0.5
console.log(0.125 * 8);    // 1

为什么?

根本原因:二进制无法精确表示某些十进制小数

计算机用二进制存储数字。十进制的 0.5 在二进制里是 0.1,能精确表示。但十进制的 0.1 在二进制里是:

scss 复制代码
0.0001100110011001100110011001100110011... (无限循环)

就像十进制里 1/3 = 0.333... 无限循环一样,0.1 在二进制里也是无限循环的。

但计算机内存有限,不能存无限长的数字,必须在某个位置截断。JavaScript 用的是 IEEE 754 双精度浮点数,只有 64 位,其中 52 位用来存小数部分。

截断就意味着误差。

哪些数能精确表示?

能被 2 的幂次整除的小数,在二进制里都能精确表示:

javascript 复制代码
// 这些都是精确的
0.5   = 1/2     = 0.1 (二进制)
0.25  = 1/4     = 0.01 (二进制)
0.125 = 1/8     = 0.001 (二进制)
0.0625 = 1/16  = 0.0001 (二进制)

而 0.1 = 1/10,10 = 2 × 5,有因子 5,所以在二进制里是无限循环。

规律:分母只包含因子 2 的分数,在二进制里能精确表示。

怎么解决?

方案一:容差比较(推荐)

既然有误差,那就别用 === 比较,改用"误差小于某个值":

javascript 复制代码
function isEqual(a, b) {
    return Math.abs(a - b) < Number.EPSILON;
}

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

Number.EPSILON 是 JavaScript 里最小的可表示精度差,约等于 2.22e-16。

方案二:转成整数算

小数不精确,整数是精确的。把小数转成整数,算完再转回来:

javascript 复制代码
// 0.1 + 0.2
const result = (0.1 * 10 + 0.2 * 10) / 10;  // 0.3

// 封装一下
function add(a, b) {
    const precision = Math.max(
        (a.toString().split('.')[1] || '').length,
        (b.toString().split('.')[1] || '').length
    );
    const factor = Math.pow(10, precision);
    return (Math.round(a * factor) + Math.round(b * factor)) / factor;
}

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

方案三:toFixed 四舍五入

简单粗暴,直接四舍五入:

javascript 复制代码
const result = parseFloat((0.1 + 0.2).toFixed(10));
console.log(result); // 0.3

注意 toFixed 返回的是字符串,要用 parseFloat 转回数字。

方案四:用专门的库

金融计算这种对精度要求高的场景,用专门的库:

javascript 复制代码
// decimal.js
import Decimal from 'decimal.js';

const a = new Decimal(0.1);
const b = new Decimal(0.2);
console.log(a.plus(b).toString()); // "0.3"

实际开发中怎么选?

场景 推荐方案
普通比较 容差比较
价格计算 转成整数(分)或用 decimal.js
显示用途 toFixed 四舍五入
科学计算 用专门的数值计算库

面试怎么答?

JavaScript 用 IEEE 754 双精度浮点数存储数字。十进制的 0.1 在二进制里是无限循环小数,但只有 52 位存储空间,必须截断,所以有精度损失。0.1 和 0.2 存储时都有微小误差,加起来误差累积,结果就不等于 0.3 了。

解决方案有几种:比较时用容差比较、计算时转成整数、或者用 decimal.js 这样的高精度库。

如果面试官继续问"IEEE 754 是什么",可以补充:

IEEE 754 是浮点数的存储标准,用 64 位存一个数字:1 位符号位、11 位指数位、52 位尾数位。这个标准是为了让不同计算机上的浮点运算结果一致。

一道相关的坑题

javascript 复制代码
console.log(0.1 + 0.2 - 0.3); // ?

答案不是 0,而是 5.551115123125783e-17

再来一道:

javascript 复制代码
console.log(9999999999999999); // ?

答案是 10000000000000000。因为超出了安全整数范围(2^53 - 1),精度丢失了。


如果你觉得这篇文章有帮助,欢迎关注我的 GitHub,下面是我的一些开源项目:

Claude Code Skills (按需加载,意图自动识别,不浪费 token,介绍文章):

全栈项目(适合学习现代技术栈):

  • prompt-vault - Prompt 管理器,用的都是最新的技术栈,适合用来学习了解最新的前端全栈开发范式:Next.js 15 + React 19 + tRPC 11 + Supabase 全栈示例,clone 下来配个免费 Supabase 就能跑
  • chat_edit - 双模式 AI 应用(聊天+富文本编辑),Vue 3.5 + TypeScript + Vite 5 + Quill 2.0 + IndexedDB
相关推荐
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅11 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊11 小时前
jwt介绍
前端
爱敲代码的小鱼11 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax