0.1+0.2!==0.3

... 2025-7-10 JavaScript
  • 数据类型
大约 2 分钟

0.1 + 0.2 === 0.3 // false
1

计算机存储数字都是以多位二进制的方式进行存储的。

而 js 没有分浮点数和整数,全部统一为 number 类型,都利用 IEEE754 标准的 64 位双精度浮点数存储。

  • 为什么会出现浮点数?

    出现浮点数的原因是在牺牲计算精度的同时来提高存储数字的范围

  • 浮点数的概念

    浮点数的产生是二进制和十进制之间换算的过程中出现了问题

IEEE 754 浮点数由三个域组成,分别为 sign bit (符号位)、exponent bias (指数偏移值) 和 fraction (分数值)。

64 位中,sign bit 占 1 位,exponent bias 占 11 位,fraction 占 52 位。

通过公式表示浮点数的值:value=signexponentfractionvalue = sign * exponent * fraction

当一个数为正数,sign bit 为 0,当为负数时,sign bit 为 1.

以 0.1 转换为 IEEE 754 标准表示为例解释一下如何求 exponent bias 和 fraction。转换过程主要经历 3 个过程:

  1. 将 0.1 转换为二进制表示
  2. 将转换后的二进制通过科学计数法表示
  3. 将通过科学计数法表示的二进制转换为 IEEE 754 标准表示

将 0.1 转换为二进制表示

0.1 的二进制表示为 0.00011...(无限重复 0011)

通过科学计数法表示

0.00011...(无限重复 0011) 通过科学计数法表示则是 1.10011001...241.10011001...*2^{-4},其中小数位中无限重复1001

转换为 IEEE 754 标准表示

当经过科学计数法表示之后,就可以求得 exponent bias 和 fraction 了。

exponent bias (指数偏移值) 等于双精度浮点数固定偏移值 (211112^{11-1}-1) 加上指数实际值(即 242^{-4} 中的 -4) 的 11 位二进制表示。为什么是 11 位?因为 exponent bias 在 64 位中占 11 位。

因此 0.1 的 exponent bias 等于 1023+(4)=10191023 + (-4) = 1019 的 11 位二进制表示,即 011 1111 1011。

再来获取 0.1 的 fraction,fraction 就是 1.10011001...(无限重复 1001) 中的小数位,由于 fraction 占 52 位所以抽取 52 位小数,1001...(中间有 11 个 1001)...1010 (请注意最后四位,是 1010 而不是 1001,因为四舍五入有进位,这个进位就是造成 0.1 + 0.2 不等于 0.3 的原因)。

到此,终于可以将 0.1 转换为 IEEE 754 表示了。

    0       011 1111 1011   1001...( 11 x 1001)...1010
(sign bit) (exponent bias)      (fraction)

1
2
3

因此不仅 0.1 会产生误差,0.2 及 0.3 也是如此,因此最后造成了 0.1 + 0.2 不等于 0.3 的结果。

上次编辑于: 2025年7月10日 04:01
贡献者: HugStars