我之前用C#从来就没有遇到过这个问题, 这难道还是问题吗? 但是前端同学应该迟早都会遇到这个问题。
如果这是这一个问题还好,还有些其他相关的问题。 比如
3 / 10 === 0.3 吗?
3 / 10000 === 0.0003 吗?
2.0 + 3.0 === 5.0 吗?
这三个都是true。奇怪吗?
这到底什么时候可能有问题? 什么时候可能没问题?这样的问题很困扰人,也很影响你写代码的效率。 总不能每次都去跑一遍吧。
相信大家只要简单一搜索,就会发现这是因为双精度浮点数的问题。 js 只有一种数字类型,就是双精度浮点数number. 这时候只能读一读官方文档了。
Number - JavaScript | MDN (mozilla.org)
文档还说在
范围内(闭区间)的整数没有问题。
但是为什么呢? 直到我去看mantissa这部分,也就是小数的二进制表示方式(毕业多年,忘光了),才恍然大悟。
Binary Fractions and Fractional Binary Numbers (electronics-tutorials.ws)
这小数再乘以2的53(左右吧)次方,不就是整数的二进制表示吗? 这个范围内的整数每个都可以没有误差的表示出来。
这个小数的二进制表示同样也说明了为什么有些小鼠不能准确表示。 0.5 可以, 0.25, 0.125等都可以。 但是0.1 用二进制怎么表示?以我的有限的数学知识,就很困,感觉是表达不准确的。
还有一个问题是 为什么 0.1 + 0.2 === 0.30000000000000004 ?而 3/10 === 0.3 ?
因为0.3 虽然不能被双精度准确表示,但是可以非常接近的表示,以至于四舍五入之后, 就等于了0.3 。 但是0.1 + 0.2 的双精度加和之后,已经到了0.30000000000000004, 这个双精度就不能舍弃了。(数学上不一定完全准确) 下面这些都是true
0.30000000000000000 === 0.3
0.30000000000000001 === 0.3
0.30000000000000002 === 0.30000000000000004
我用工具把十进制转成双精度二进制,发现0.30000000000000000和0.30000000000000001 的二进制表示是一样的。 而0.30000000000000002 和 0.30000000000000004 也是一样的。
工具: [Double (IEEE754 Double precision 64-bit) (binaryconvert.com)]Double (IEEE754 Double precision 64-bit) (binaryconvert.com)
有些语言有decimal类型, 比如C#, 就不用操心这个问题。还有些语言比如go, java跟js一样,都没有decimal, 就需要解决这个问题。
这篇文章有些地方可能有出入,欢迎指正。 有写得更好的文章,也欢迎推荐。
最后, 如果我们需要计算数字,那我们该怎么办呢? 我的答案是使用现有的库。至于怎么选库,可以网上搜文章,简单来说用big.js就行了。
推荐文章: dev.to/fvictorio/a…
下面三个库的作者也解释了区别: github.com/MikeMcl/big…
| Library | Size | Weekly downloads |
|---|---|---|
| big.js | 8K | 9.272.986 |
| bignumber.js | 20K | 2.390.156 |
| decimal.js | 32K | 290.392 |