问题
首先JS里面0.1 + 0.2 !== 0.3
已经算是常识了,因为不只是JS,Java和其他语言中也存在类似的问题,就是浮点数精度不准确问题。
但是由于我们项目的特殊情况,需要使用超过16位的数字时发现,JS竟然大数也会丢失精度。
比如说console.log(123456789012345678901)
的结果竟然是123456789012345680000
这就让人非常不理解。因为好像数字溢出时变为Infinity/NaN
/抛错/直接变为某个最大固定值才更符合直觉。
原因
这个原因主要是JS采用的IEEE754规范规定了double型(注意JS中不存在int/float概念,默认都是double)一共64位,其中1位符号位,11位表示指数E,剩下52位为有效数字。
这也就导致JS的数字只有在[-(Math.pow(2,53)-1), Math.pow(2,53)-1]
内的数字才是符合IEEE754规范的。
所以JS(ES6+)也提供了最大/最小安全数Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
, 以及方法Number.isSafeInteger()
以供对比。
注意!
位运算
|
&
~
^
和移位运算>>
<<
>>>
<<<
都是基于32位整数的,所以涉及到位运算时,请注意不要超过Math.pow(2,31)-1也就是2147483647
(真正转变为Infinity
的数其实也是有的,就是大于等于Math.pow(2,1024)
解决方式
首先JS的标准是不能够去改变的,那么现在说一下常见的解决思路
纯数字的id/key/订单编号等
改用string类型,前后端传值时进行约定
巨大的金额,比如津巴布韦币 10万亿津元兑换2美分
建议根据地区使用不同的基础单位,如果金额巨大,基本单位可为万/千等,或者对位数进行拆分
ES10引入BigInt
BigInt 是ES10新引入的内建对象,可创建超过(Math.pow(2,53)-1)
的大数,表现为在数字后面多加一个n
,就像123456789012345678901n