科学社会主义者 發表於 2026-3-17 15:14:00

高精度计算插件 decimal.js 处理 JS 浮点数精度问题(0.1 + 0.2 !== 0.3)

<p>JS 中的浮点数计算经常会看到这样问题:为什么 0.1 + 0.2 不等于 0.3 ?</p>
<p>在浏览器控制台执行 0.1 + 0.2 会得到一个奇怪的结果:<strong>0.30000000000000004</strong></p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202603/596097-20260317151318924-174427342.png"></p>
<p>为何结果会是这么奇怪的一个数字?人类瞄一眼就知道的结果,为啥交给 JS 会得出这么奇怪的结果?</p>
<p>都知道程序的世界就是二进制的天下,在电脑的 CPU 运算时,所有的十进制都需要转为二进制计算。</p>
<h2 id="十进制转二进制">十进制转二进制</h2>
<p>十进制整数转二进制一般使用除 2 取余法,比如 35 换算二进制:</p>
<pre><code class="language-js">35 / 2 = 17 余数 1
17 / 2 = 8 余数 1
8 / 2 = 4 余数 0
4 / 2 = 2 余数 0
2 / 2 = 1 余数 0
1 / 2 = 0 余数 1
</code></pre>
<p>最终结果将余数倒序排列得到 <strong>100011</strong></p>
<hr>
<p>而小数换算二进制则使用乘 2 取整法,比如 0.03125 :</p>
<pre><code class="language-js">0.03125 * 2 = 0.0625 取整 0
0.0625 * 2 = 0.125 取整 0
0.125 * 2 = 0.25 取整 0
0.25 * 2 = 0.5 取整 0
0.5 * 2 = 1 取整 1
</code></pre>
<p>将最终的计算结果正序排列得到 <strong>0.00001</strong></p>
<hr>
<p>除了整数和小数外,还涉及到负数转换,有兴趣可以了解下 <code>IEEE 754</code> 标准(JS 数值运算基于 IEEE 754 标准)。</p>
<h2 id="01-与-02">0.1 与 0.2</h2>
<p>各种编程语言的 0.1 + 0.2 结果:https://0.30000000000000004.com/#ada</p>
<p>至于为什么会是 <strong>0.30000000000000004</strong> 这个结果,百度一搜一大把的文章,本文就不再赘述。</p>
<p>一句话总结就是 0.1 和 0.2 在转为 <code>二进制</code> 后是一个无限循环的结果(类似十进制中的 1/3),而 <code>IEEE 754</code> 标准中储存位数是有限的,在处理这种无限循环的时,会进行舍入处理,就会造成计算精度丢失,在一系列 <code>舍入</code> 和 <code>规格化数</code> 后就得出了这么一个结果。</p>
<h2 id="处理办法">处理办法</h2>
<p>同一个作者,写了三个这种运算模块,npm 的周下载量都在千万级别:</p>
<p>big.js github 地址:https://github.com/MikeMcl/big.js<br>
npm 周下载量 2 千万左右:</p>
<p>bignumber.js github 地址:https://github.com/MikeMcl/bignumber.js<br>
npm 周下载量 1.5 千万左右:</p>
<p>decimal.js github 地址:https://github.com/MikeMcl/decimal.js<br>
npm 周下载量: 2 千万左右。</p>
<p><strong>三者区别:</strong></p>
<p>官方文档:https://github.com/MikeMcl/big.js/wiki</p>
<p>总结:</p>
<p>包体积 big.js &lt; bignumber.js &lt; decimal.js</p>
<p><code>big.js</code> 适合基础的十进制运算,比如简单的金融计算等。<br>
<code>bignumber.js</code> 支持二进制运算,适合一些加密计算场景。<br>
<code>decimal.js</code> 支持二进制和三角函数运算,适合一些科学计算场景。</p>
<hr>
<p><strong>其他作者写的数学计算模块:</strong><br>
https://github.com/josdejong/mathjs</p>
<p>star 数量: 14762<br>
npm 周下载量: 1 百万左右</p>
<p>瞅了一眼,好像内部也是依赖的 <code>decimal.js</code></p>
<hr>
<p>使用示例:</p>
<pre><code class="language-html">&lt;script src="https://registry.npmmirror.com/big.js/7.0.1/files/big.js"&gt;&lt;/script&gt;
&lt;script src="https://registry.npmmirror.com/bignumber.js/9.3.0/files/bignumber.js"&gt;&lt;/script&gt;
&lt;script src="https://registry.npmmirror.com/decimal.js/10.5.0/files/decimal.js"&gt;&lt;/script&gt;

&lt;script&gt;
(() =&gt; {
// 加法
console.log('原生 JS 计算:', 0.1 + 0.2) // 0.30000000000000004
// 减法
console.log('原生 JS 计算:', 0.3 - 0.1) // 0.19999999999999998
// 乘法
console.log('原生 JS 计算:', 0.2 * 0.1) // 0.020000000000000004
// 除法
console.log('原生 JS 计算:', 0.3 / 0.1) // 2.9999999999999996
})();
(() =&gt; {
const x = new Big(0.1)
const y = new Big(0.2)
const z = new Big(0.3)
// 加法
console.log('big.js 插件计算:', x.plus(y).toNumber()) // 0.3
console.log('big.js 插件计算:', x.plus(y).toString()) // '0.3'
// 减法
console.log('big.js 插件计算:', z.minus(x).toNumber()) // 0.2
// 乘法
console.log('big.js 插件计算:', x.times(y).toNumber()) // 0.02
// 除法
console.log('big.js 插件计算:', z.div(x).toNumber()) // 3
})();
(() =&gt; {
const x = new BigNumber(0.1)
const y = new BigNumber(0.2)
const z = new BigNumber(0.3)
// 加法
console.log('bignumber.js 插件计算:', x.plus(y).toNumber()) // 0.3
console.log('bignumber.js 插件计算:', x.plus(y).toString()) // '0.3'
// 减法
console.log('bignumber.js 插件计算:', z.minus(x).toNumber()) // 0.2
// 乘法
console.log('bignumber.js 插件计算:', x.times(y).toNumber()) // 0.02
// 除法
console.log('bignumber.js 插件计算:', z.div(x).toNumber()) // 3
})();
(() =&gt; {
const x = new Decimal(0.1)
const y = new Decimal(0.2)
const z = new Decimal(0.3)
// 加法
console.log('decimal.js 插件计算:', x.plus(y).toNumber()) // 0.3
console.log('decimal.js 插件计算:', x.plus(y).toString()) // '0.3'
// 减法
console.log('decimal.js 插件计算:', z.minus(x).toNumber()) // 0.2
// 乘法
console.log('decimal.js 插件计算:', x.times(y).toNumber()) // 0.02
// 除法
console.log('decimal.js 插件计算:', z.div(x).toNumber()) // 3
})();
&lt;/script&gt;
</code></pre>
<p>也可以使用 npm 安装使用</p>
<pre><code class="language-js">// npm install big.js
// const Big = require('big.js');
import Big from 'big.js';

const x = new Big(0.1)
</code></pre>
<p><em>原生 JS 要处理浮点数计算也能做,只是比较麻烦,需要将计算的小数转为整数之后再进行计算,最后除以倍数。比如 0.1 + 0.2 可以转为 (1+2) / 10。</em></p>
<hr>
<p>JS 支持的 <strong>最大整数</strong> 也是有边界的,在 <code>−2的53次方 + 1</code> 到 <code>2的53次方 − 1</code> 之间(即 -9007199254740991 到 9007199254740991)。<br>
超出这个边界计算也会出问题,比如:</p>
<pre><code class="language-js">(() =&gt; {
console.log('超出边界计算', 2**53) // 9007199254740992
console.log('超出边界计算', 2**53 - 1) // 9007199254740991
console.log('超出边界计算', 2**53 - 1.1) // 9007199254740991
console.log('超出边界计算', 2**53 + 1) // 9007199254740992
console.log('超出边界计算', 2**53 + 1.1) // 9007199254740994
})();
</code></pre>
<p>超大数计算也可以使用 big.js 处理:</p>
<pre><code class="language-js">(() =&gt; {
const x = (new Big(2)).pow(53)
const y = new Big(1.1)
console.log('加法超出边界计算', x.plus(y).toString()) // 9007199254740993.1
console.log('减法超出边界计算', x.minus(y).toString()) // 9007199254740990.9
})();
</code></pre>
<h2 id="写在最后">写在最后</h2>
<p>凡使用 <code>IEEE 754</code> 标准的编程语言,都存在浮点数计算问题,只是其他语言有内置的解决方案,而 JS 最初的设计思想是用于浏览器交互,所以没有内置解决方案,好在有热心作者开源的插件用于解决浮点数计算问题。</p>
<p>在使用 JS 计算时,需特别小心浮点数问题,能交给后端处理就交出去~~</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>&nbsp;</p>
<p style="font-size: 18px;font-weight: bold;">文章首发于微信公众号【<span style="color:rgb(255, 71, 87)">前端路引</span>】,欢迎 <span style="color:#4ec259">微信扫一扫</span> 查看更多文章。</p>
<p>
<img style="max-width: 320px;" src="https://images.cnblogs.com/cnblogs_com/linx/2447020/o_250228035031_%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BA%8C%E7%BB%B4%E7%A0%81.png"/>
</p>
<p>本文来自博客园,作者:前端路引,转载请注明原文链接:https://www.cnblogs.com/linx/p/19729761</p>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/linx/p/19729761
頁: [1]
查看完整版本: 高精度计算插件 decimal.js 处理 JS 浮点数精度问题(0.1 + 0.2 !== 0.3)