先上结论:0.1+0.2 == 0.3 在JavaScript中是错误的。
原因
系统学习过计算机基础理论知识的都应该知道,计算机中的数据是以二进制存储的。相关的,我们想要比较两个数的大小,计算机又是怎样实现的呢,总不可能是直接像人脑一样经过看似简单实则复杂的方式进行对比。计算机的思考方式是非常简单,非0即1(没别的意思 /狗头)。
首先来看Javascript是如何存储小数的
JavaScirpt 使用 Number 类型来表示数字(整数或浮点数),遵循 IEEE 754 标准,通过 64 位来表示的一个number类型变量。
IEEE 754 标准定义如何用二进制来存储浮点数,根据公式:
V=(-1)N x M x 2^R(128=(-1)^(0)x1x2^7)
其中:
- N=为符号位,N=1,则为负数;N=0,则为正数
- M=底数(尾数)
- R=幂(指数)
三部分(N、M、R)分开存储组成一个浮点数。64位分别表示:1 符号位, 0 表示正数, 1 表示负数 N52 尾数,小数部分(即有效数字) M11 指数位(e) R
计算过程
我们试着将0.1+0.2按照计算机的方式进行处理。
1.第一步,计算机首先将0.1和0.2解析成对应的二进制数然后存储。
附一个链接可根据IEEE754 转换二进制:在线IEEE浮点二进制计算器工具 - ToolTT在线工具箱
0.1换成二进制为:0.0001100110011001100110011001100110011001100110011001101(无限循环)
0.2换成二进制为:0.0011001100110011001100110011001100110011001100110011011(无限循环)
----------------------------------------------------------------------------------
将无限循环的尾数截断至52位
根据公式可以转换成:0.1=(-1)^0*1.1001100110011001100110011001100110011001100110011010(52)*2^(-4)
根据公式可以转换成:0.2=(-1)^0*1.1001100110011001100110011001100110011001100110011010(52)*2^(-3)
2.第二步,想要计算0.1+0.2,需要进行对阶,即将指数部分取值相同,然后才能尾数相加。
对阶尾数后:
0.1=(-1)^0*0.1100110011001100110011001100110011001100110011001101(52)*2^(-3)
0.2=(-1)^0*1.1001100110011001100110011001100110011001100110011010(52)*2^(-3)
3.第三步尾数相加生成结果。
尾数相加之后为: 10.0110011001100110011001100110011001100110011001100111(52)
转换成标准格式为:1.00110011001100110011001100110011001100110011001100111(53)
*****注意这里,现在的尾数是53位,不满足52位尾数的要求
*****所以需要截断一位,js引擎在这里截断的结果是:
1.0011001100110011001100110011001100110011001100110100(52)
相加之后结果表示为:
0.1+0.2=
(-1)^0*1.0011001100110011001100110011001100110011001100110100(52)*2^(-2)
转换成十进制结果是:0.30000000000000004,结果并不等于0.3。
结果显示为: 0.30000000000000004,与我们在nodejs环境下计算的结果一致。