初学JavaScript最让人头疼的莫过于两件事:
一、浮点数的计算;
二、异步获取数据;
今天咱们就来了解一下 这两个大问题其中的浮点数计算问题
一、浮点数计算
例如经典的0.1+0.2问题
按照常规的答案为0.3,的确,在小学课本以及大学课本的答案都是0.3;
在各大语言(java、php、python、go、c等等)答案也是0.3;
为什么单单在JavaScript语言中就不是了,曾经我就这个答案骂过*****(你们懂得)
言归正传,开始分析问题
搞清楚这个问题前要懂得两个基本知识点
1、十进制转化二进制
2、数字在JavaScript内部是如何存储的
转化二进制是各种高级语言到最原生的机器代码必经之路(毕竟电脑只认识0和1),
十进制转化二进制的方法在不同语言中各不相同,但是逻辑思想是一致的
上学的时候老师教过转化方法
下面就手动撸下0.1、0.2转化成二进制的过程(此处是重点,打个✳标记)
整数部分 除二倒取余,
小数部分 乘二取整
二进制结果只包含0和1两个数字
十进制100转化成二进制 结果1100100
过程:
100/2=50 (余数0)
50/2=25 (余数0)
25/2=12 (余数1)
12/2=6 (余数0)
6/2=3 (余数0)
3/2=1 (余数1)
1/2=0 (余数1)
十进制0.2转化成二进制 结果0.001100110011.....(无限循环小数)
过程
0.2*2=0.4 (取整数 0 余0.4)
0.4*2=0.8 (取整数 0 余0.8)
0.8*2=1.6 (取整数 1 余0.6)
0.6*2=1.2 (取整数 1 余0.2)
0.2*2=0.4 (取整数 0 余0.4)
0.4*2=0.8 (取整数 0 余0.8)
0.8*2=1.6 (取整数 1 余0.6)
0.6*2=1.2 (取整数 1 余0.2)
......001100110011...
十进制0.1转化成二进制 结果0.0001100110011....(无限循环小数)
过程
0.100*2=0.2 (取整数 0 余0.2)
0.200*2=0.4 (取整数 0 余0.4)
0.400*2=0.8 (取整数 0 余0.8)
0.800*2=1.6 (取整数 1 余0.6)
0.600*2=1.2 (取整数 1 余0.2)
0.200*2=0.4 (取整数 0 余0.4)
0.400*2=0.8 (取整数 0 余0.8)
0.800*2=1.6 (取整数 1 余0.6)
0.600*2=1.2 (取整数 1 余0.2)
......001100110011...
JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此
第1位:符号位,0表示正数,1表示负数
第2位到第12位(共11位):指数部分
第13位到第64位(共52位):小数部分(即有效数字)
上面公式是正常情况下(指数部分在0到2047之间),一个数在 JavaScript 内部实际的表示形式。
精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-253到253,都可以精确表示。
显然0.1和0.2在转化成二进制时时无限循环小数,在53位小数后被中断,后面的已经被忽略掉 在转换过程中就造成了不精确的数
所以两个不精确的数相加后结果必然不等于3
如何避免这种情况的发生呢
通常的解决办法是将0.110+0.210,再将结果除10 就是正确的3
验证
0.1*10=1
1转换成二进制 结果为01
1/2=0(余数是1)
0.2*10=2
2转换成二进制 结果为10
2/2=1 (余数0)
1/2=0 (余数1)
二进制 01+10=11
二进制转化十进制 1*2^0+1*2^1=3
3/10=0.3
二、解决异步
promise
async。。。await
回掉函数等等
后面篇章在详解