0.1加0.2为什么不等于0.3
<h1 data-id="heading-0">🧑💻 写在开头</h1><p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<h2 data-id="heading-0">0.1 + 0.2 为什么不等于 0.3?答不上来的都挂了</h2>
<p>这个问题你可能在面试、线上 Bug、甚至随手写 Demo 的时候都见过:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">console.log(0.1 + 0.2 === 0.3); // false</pre>
</div>
<div>
<div>
<p>很多人第一反应是“浮点数精度问题”,但如果继续追问:</p>
<ul>
<li>为什么偏偏是 <code>0.1</code>、<code>0.2</code> 这种小数出问题?</li>
<li>“精度”到底精在哪一位、丢在哪一步?</li>
<li>实际开发里应该怎么比较、怎么计算才稳?</li>
</ul>
<p>这篇文章按“现象 → 原因 → 解决 → 面试回答”的顺序,把它讲透。</p>
<h3 data-id="heading-1">先看现象</h3>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">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</pre>
</div>
<p>但有些计算又是对的:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">console.log(0.5 + 0.5); // 1
console.log(0.25 + 0.25);// 0.5
console.log(0.125 * 8); // 1</pre>
</div>
<p>为什么?</p>
<h3 data-id="heading-2">根本原因:二进制无法精确表示某些十进制小数</h3>
<p>计算机用二进制存储数字。十进制的 0.5 在二进制里是 0.1,能精确表示。但十进制的 0.1 在二进制里是:</p>
<div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">0.0001100110011001100110011001100110011... (无限循环)</pre>
</div>
<div>
<div>
<p>就像十进制里 1/3 = 0.333... 无限循环一样,0.1 在二进制里也是无限循环的。</p>
<p>但计算机内存有限,不能存无限长的数字,必须在某个位置截断。JavaScript 用的是 IEEE 754 双精度浮点数,只有 64 位,其中 52 位用来存小数部分。</p>
<p>截断就意味着误差。</p>
<h3 data-id="heading-3">哪些数能精确表示?</h3>
<p>能被 2 的幂次整除的小数,在二进制里都能精确表示:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 这些都是精确的
0.5 = 1/2 = 0.1 (二进制)
0.25= 1/4 = 0.01 (二进制)
0.125 = 1/8 = 0.001 (二进制)
0.0625 = 1/16= 0.0001 (二进制)</pre>
</div>
<p>而 0.1 = 1/10,10 = 2 × 5,有因子 5,所以在二进制里是无限循环。</p>
<p>规律:分母只包含因子 2 的分数,在二进制里能精确表示。</p>
<h3 data-id="heading-4">怎么解决?</h3>
<h4 data-id="heading-5">方案一:容差比较(推荐)</h4>
<p>既然有误差,那就别用 <code>===</code> 比较,改用"误差小于某个值":</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">function isEqual(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
console.log(isEqual(0.1 + 0.2, 0.3)); // true</pre>
</div>
<p><code>Number.EPSILON</code> 是 JavaScript 里最小的可表示精度差,约等于 2.22e-16。</p>
<h4 data-id="heading-6">方案二:转成整数算</h4>
<p>小数不精确,整数是精确的。把小数转成整数,算完再转回来:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 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('.') || '').length,
(b.toString().split('.') || '').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</pre>
</div>
<h4 data-id="heading-7">方案三:toFixed 四舍五入</h4>
<p>简单粗暴,直接四舍五入:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const result = parseFloat((0.1 + 0.2).toFixed(10));
console.log(result); // 0.3</pre>
</div>
<p>注意 <code>toFixed</code> 返回的是字符串,要用 <code>parseFloat</code> 转回数字。</p>
<h4 data-id="heading-8">方案四:用专门的库</h4>
<p>金融计算这种对精度要求高的场景,用专门的库:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 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"</pre>
</div>
<h3 data-id="heading-9">实际开发中怎么选?</h3>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202603/2149129-20260330124422534-325828007.png" alt="ScreenShot_2026-03-30_124410_768" loading="lazy"></p>
<div>
<div>
<h3 data-id="heading-10">面试怎么答?</h3>
<blockquote>
<p>JavaScript 用 IEEE 754 双精度浮点数存储数字。十进制的 0.1 在二进制里是无限循环小数,但只有 52 位存储空间,必须截断,所以有精度损失。0.1 和 0.2 存储时都有微小误差,加起来误差累积,结果就不等于 0.3 了。</p>
<p>解决方案有几种:比较时用容差比较、计算时转成整数、或者用 decimal.js 这样的高精度库。</p>
</blockquote>
<p>如果面试官继续问"IEEE 754 是什么",可以补充:</p>
<blockquote>
<p>IEEE 754 是浮点数的存储标准,用 64 位存一个数字:1 位符号位、11 位指数位、52 位尾数位。这个标准是为了让不同计算机上的浮点运算结果一致。</p>
</blockquote>
</div>
<div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
</div>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p>
</div>
</div>
</div>
</div><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19794945
頁:
[1]