0.1 + 0.2 为什么不等于 0.3

JavaScript内部计算都是以二进制进行的

  • 整数部分转换为二进制的规则是:除以2取余,余数逆序排列。
10 / 2 = 5 ... 0  // ↑
5 / 2 = 2 ... 1   // ↑
2 / 2 = 1 ... 0   // ↑
1 / 2 = 0 ... 1   // ↑

所以10转换为二进制就是1010

  • 小数部分转换为二进制的规则是:乘2取整,整数部分顺序排列。
0.1 * 2 = 0.2 // 整数0 ↓
0.2 * 2 = 0.4 // 整数0 ↓
0.4 * 2 = 0.8 // 整数0 ↓
0.8 * 2 = 1.6 // 整数1 ↓
0.6 * 2 = 1.2 // 整数1 ↓
0.2 * 2 = 0.4 // 整数0 ↓
0.4 * 2 = 0.8 // 整数0 ↓
0.8 * 2 = 1.6 // 整数1 ↓
0.6 * 2 = 1.2 // 整数1 ↓
// 无限循环

小数部分转换为二进制可能出现无限循环,比如0.1的二进制表示为0.0001100110011...(0011循环)。

JavaScript中number类型使用IEEE754标准64位存储

IEEE754标准64位存储也就是双精度浮点数(double),系统会为每个数值分配64位存储空间,以科学计数法的方式存储(1.xx * 2 ^ n)。

64位存储空间分为3个部分:

1 * 符号位(sign) + 11 * 指数位(exponent) + 52 * 小数位(fraction)

  • 符号位:0正数,1负数
  • 指数位:固定值1023 + 指数实际值
    • (2 ^ (e - 1)) - 1 => (2 ^ (11 - 1)) => 1023
  • 小数位:二进制小数点后的数字

IEEE754标准64位数相加

总结

  • JavaScript的Number类型的实现遵循IEEE754标准,以64位双精度浮点数存储,即计算机最多存储64位二进制数
  • 小数在转换成二进制表示的时候,可能会存在无限循环的情况,但是由于最多只能存储64位二进制数,就只能舍入(0舍1入),这就发生了第一次精度丢失。

参考

Last Updated: