从一道面试题"0.1+0.2 === 0.3",看JavaScript数值Number类型解析
分析(原来JS的Number是Double)
0.1 + 0.2 // 0.30000000000000004;
0.1+0.2 === 0.3 // false
从代码的运行结果来看,显然0.1+0.2是不等于0.3的那么导致这种结果的原因是什么呢?
JavaScript权威指南第六版-第3章类型”说明,在JavaScript当中对于数字Number类型的表示采用的是 “IEEE 754 标准
定义的双精度64位格式” 和其他编程语言(如C,Java)不同,JavaScript不区分整数值和浮点数值,所有数值在JavaScript
中均用双精度浮点数值表示(相当于C,Java中的double),所以在进行数字运算的时候要特别注意精度缺失问题。
IEEE 754标准
简单介绍“IEEE 754”规范,IEEE 754”采用双精度存储(double-precision 64-bit format IEEE 754 values)
占用64 bit
意义:
- s(sign) 1位用来表示符号位(正负数)
- e(exponent) 11位用来表示指数
- m(mantissa) 52位用来表示尾数
JavaScript中数字的存储机制
(s) * (m) * (2^e)
那么浮点数在运算时又为什么会造成精度缺失呢?
0.1 >> 0.0001 1001 1001 1001...无限循环
0.2 >> 0.0011 0011 0011 0011...无限循环
由于存储空间的有限,于是只能模仿十进制进行四舍五入了,但是二进制只有 0 和 1 两个,于是变为 0 舍 1 入。这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。
大整数的精度丢失
大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。
大于 9007199254740992 的可能会丢失精度
9007199254740992 + 1
// 丢失 9007199254740992
9007199254740992 + 2
// 未丢失 9007199254740994
9007199254740992 + 3
// 丢失 9007199254740996
9007199254740992 + 4
// 未丢失 9007199254740996
回到问题
那么为什么0.1+0.2不等于0.3呢?
- 因为 计算机 的存储原理,造成计算机在存储浮点数时,存储的不是准确数值,存储的是一个近似数值,显示时,显示为一个浮点数值效果;
- 当浮点数直接参与计算或者参与比较时,实际参与预算或者比较的数值,也是近似值;
- 就造成了计算或者比较时,一定会存在误差,这个误差在特殊情况下会表现出误差的结果;
ps:JavaScript中规定,即使使用科学计数法,数据类型也是浮点数类型,浮点数的误差/浮点数的精确丢失
模拟计算
先将 0.1 和 0.2 转化成二进制,对于十进制转二进制,整数部分除二取余,倒序排列
,小数部分乘二取整,顺序排列
0.1 转化为二进制0.0 0011 0011 0011 0011 0011 0011 … (0011循环)
0.2 转化为二进制0.0011 0011 0011 0011 0011 0011 0011 … (0011循环)
然后根据IEEE 754标准 (s) * (m) * (2^e)
来表示
// 0.1
e = -4;
m = 1.1001100110011001100110011001100110011001100110011010 (52位)
// 0.2
e = -3;
m = 1.1001100110011001100110011001100110011001100110011010 (52位)
//这里的m指的是小数点后的52位,e为m的指数,小数点前的整数部分就是隐藏位s来表示符号
//如果发现指数e不一致时,一般采用右移,因为即使右边溢出了,损失的精度远远小于左移时的溢出
//转化之后进行求和
e = -3; m = 0.1100110011001100110011001100110011001100110011001101 (52位)
+
e = -3; m = 1.1001100110011001100110011001100110011001100110011010 (52位)
// 得到
e = -3; m = 10.0110011001100110011001100110011001100110011001100111 (52位)
// 保留一位整数
e = -2; m = 1.00110011001100110011001100110011001100110011001100111 (53位)
// 发现超过了52位,于是要做四舍五入,因为无法区分哪个更接近,于是规则是保留偶数的一个,得到最终的二进制数
m=1.0011001100110011001100110011001100110011001100110100 (52位)
// 然后得到最终的二进制数
1.0011001100110011001100110011001100110011001100110100 * 2^-2 = 0.010011001100110011001100110011001100110011001100110100
// 现在转化为十进制,二进制小数转化为十进制的方法是小数点后 第一位 *2 ^ -1,第二位 *2 ^ -2,以此类推
// 可以利用等比数列求和公式,最终求得十进制数为0.30000000000000004
结论
所以0.1 + 0.2 的最终结果是0.30000000000000004
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!