如何用decimal.js插件解决JS浮点数0.1+0.2≠0.3的怪异现象?
摘要:JS 中的浮点数计算经常会看到这样问题:为什么 0.1 + 0.2 不等于 0.3 ? 在浏览器控制台执行 0.1 + 0.2 会得到一个奇怪的结果:0.30000000000000004 为何结果会是这
JS 中的浮点数计算经常会看到这样问题:为什么 0.1 + 0.2 不等于 0.3 ?
在浏览器控制台执行 0.1 + 0.2 会得到一个奇怪的结果:0.30000000000000004
为何结果会是这么奇怪的一个数字?人类瞄一眼就知道的结果,为啥交给 JS 会得出这么奇怪的结果?
都知道程序的世界就是二进制的天下,在电脑的 CPU 运算时,所有的十进制都需要转为二进制计算。
十进制转二进制
十进制整数转二进制一般使用除 2 取余法,比如 35 换算二进制:
35 / 2 = 17 余数 1
17 / 2 = 8 余数 1
8 / 2 = 4 余数 0
4 / 2 = 2 余数 0
2 / 2 = 1 余数 0
1 / 2 = 0 余数 1
最终结果将余数倒序排列得到 100011
而小数换算二进制则使用乘 2 取整法,比如 0.03125 :
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
将最终的计算结果正序排列得到 0.00001
除了整数和小数外,还涉及到负数转换,有兴趣可以了解下 IEEE 754 标准(JS 数值运算基于 IEEE 754 标准)。
0.1 与 0.2
各种编程语言的 0.1 + 0.2 结果:https://0.30000000000000004.com/#ada
至于为什么会是 0.30000000000000004 这个结果,百度一搜一大把的文章,本文就不再赘述。
一句话总结就是 0.1 和 0.2 在转为 二进制 后是一个无限循环的结果(类似十进制中的 1/3),而 IEEE 754 标准中储存位数是有限的,在处理这种无限循环的时,会进行舍入处理,就会造成计算精度丢失,在一系列 舍入 和 规格化数 后就得出了这么一个结果。
处理办法
同一个作者,写了三个这种运算模块,npm 的周下载量都在千万级别:
big.js github 地址:https://github.com/MikeMcl/big.js
npm 周下载量 2 千万左右:
bignumber.js github 地址:https://github.com/MikeMcl/bignumber.js
npm 周下载量 1.5 千万左右:
decimal.js github 地址:https://github.com/MikeMcl/decimal.js
npm 周下载量: 2 千万左右。
三者区别:
官方文档:https://github.com/MikeMcl/big.js/wiki
总结:
包体积 big.js < bignumber.js < decimal.js
big.js 适合基础的十进制运算,比如简单的金融计算等。
bignumber.js 支持二进制运算,适合一些加密计算场景。
decimal.js 支持二进制和三角函数运算,适合一些科学计算场景。
