为什么 0.1 + 0.2 != 0.3?如何解决这个问题?
JS 数字精度
JS 采用 IEEE 754
双精度版本(64位) 只要采用 IEEE 754
的语言都有该问题
我们都知道计算机是通过二进制来存储东西的,那么 0.1 在二进制中会表示为
js
// (0011) 表示循环
0.1 = 2^-4 * 1.10011(0011)
0.1 在二进制中是无限循环的一些数字 很多十进制小数用二进制表示都是无限循环的
IEEE 754
双精度版本(64位)将 64 位分为了三段
- 第一位用来表示符号
- 接下去的 11 位用来表示指数
- 其他的位数用来表示有效位,也就是用二进制表示 0.1 中的 10011(0011)
这些循环的数字被裁剪了,就会出现精度丢失的问题 也就造成了 0.1 不再是 0.1 了,而是变成了 0.100000000000000002
简单来说 JS把编写代码的十进制数字
编译为计算机语言二进制
时存不了无限的值(精确),而裁剪成了64位的二进制值(不精确),JS用这些裁剪后的二进制数字进行运算或直接输出都不是计算机语言二进制的真实数字
js
0.100000000000000002 === 0.1 // true
同样的,0.2 在二进制也是无限循环的,被裁剪后也失去了精度变成了 0.200000000000000002
js
0.200000000000000002 === 0.2 // true
这两者相加不等于 0.3 而是 0.300000000000000004
js
0.1 + 0.2 != 0.3 // true
0.1 + 0.2 === 0.30000000000000004 // true
🤔 既然 0.1
不是 0.1
,那为什么 console.log(0.1)
输出的不是 0.100000000000000002
二进制被转换为了十进制,十进制又被转换为了字符串,在这个转换的过程中发生了取近似值的过程,所以打印出来的其实是一个近似值
js
console.log(0.1) // 0.1
console.log(0.10000000000000002) // 0.10000000000000002
console.log(0.100000000000000002) // 0.1
console.log(0.1000000000000000091) // 0.1
😖 取近似值的这个动作,有什么用,还不如输出真实值 因为JS存不起来吗? 而且不报错,所以存了个近似值处理,
解决JS数字精度问题
简单的保留一位小数
解决办法
js
parseFloat((0.1 + 0.2).toFixed(10)) === 0.3 // true
BigNumber
TODO: