1、问题
给定一个变量 temp, 变量初始值为0.1,每次递增0.1:
let temp = 0.1;
while (temp<10){
temp+=0.1;
console.log(temp)
}
打印值部分序列如下:
0.4>0.5>0.6>0.7>0.799999…>0.899999…>0.9999…>1.09999…>0.9999…>1.09999…>0.99999…
注意到从0.7>0.7999…的部分开始,0.7+0.1=0.7999999999
继续看以下代码:
<html>
<script type="text/javascript" >
window.onload =function(){
alert(0.4==0.40000000000000001);//true
}
</script>
</html>
很神奇
大概试了一下,从小数位后18位起,计算机就识别不了了。
2、解释
典型的精度问题,其实不是我们代码本身的问题。
计算机是以二进制的方式来存储数据,代码中的十进制小数,计算机都会替我们转换为二进制去进行计算。
作为小数,在二进制的情形下,会出现无穷小数的状况。
举个例子
十进制15.43转换成二进制是多少
整数部分:
15%2=1
(15%2)%2=1
((15%2)%2)%2=1
(((15%2)%2)%2)%2=1
除二取余,逆序排列:1111
小数部分:
Math.ceil(0.43*2)=0
Math.ceil((0.43*2)*2)=1
Math.ceil(((0.43*2)*2)*2)=1
Math.ceil((((0.43*2)*2)*2)*2)=0
Math.ceil(((((0.43*2)*2)*2)*2)*2)=1
Math.ceil((((((0.43*2)*2)*2)*2)*2)*2)……
……
乘二取整,顺序排列:01101……
把计算出来的整数部分和小数部分放到一起,中间加上小数点
所以15.43二进制为:1111.01101……
可以发现,在计算整数部分的过程中,2的0次方=1,这是整数最小单位,二进制整数以"0"或"1"结尾,相应10进制则表现为奇数或者偶数,涵盖了所有自然数,所以不会出现无穷序列的情况。
但小数部分如果不正好等于0.5的n次幂,如例子中的0.43,则二进制转换的这个过程永远无法结束。
3、解决方案
所以小数的计算,在项目中进行的应用时,不能直接将其交给计算机进行处理,否则大概率会出问题。
人工干预的方法:将小数类型数据转化成0.5的n次幂家族中的一员,n由项目实际所需的精度而定。