从0.1+0.2=0.30000000000000004再看JS中的Number类型写在前面今天在看《JavaScript高级程序设计》的时候,注意到书中特意提到了0.1+0.2=0.30000000000000004这样一个浮点数计算错误的问题,觉得很有意思。平时在工作中对于浮点数了解地并不多,正好最近小组同学也遇到了这个问题,准备来总结下这个看似简单的Number基础类型,其实并不简单。这篇博客意在从这个奇怪的计算结果去学习总结浮点数的相关知识。两个既定的事实在JS中能否表示的数字的绝对值范围是5e-324 ~ 1.7976931348623157e+308,这一点可以通过Number.MAX_VALUE和Number.MIN_VALUE来得到证实在JS中能够表示的最大安全整数的范围是:-9007199254740991 ~ 9007199254740991,这一点可以通过Number.MIN_SAFE_INTEGER和Number.MAX_SAFE_INTEGER来求证两个存在的问题在四则运算中存在精度丢失的问题,比如: 01 + 0.2 //0.30000000000000004超过最大安全整数的运算是不安全的,比如:9007199254740991 + 2 // 9007199254740992Why?想要解释清楚上述的两个事实和问题,需要先知道小数在计算机中是如何存储的:知识点!!!把这个浮点数转成对应的二进制数,并用科学计数法表示把这个数值通过IEEE 754标准表示成真正会在计算机中存储的值我们知道,JS中的Number类型使用的是双精度浮点型,也就是其他语言中的double类型。而双精度浮点数使用64 bit来进行存储,结构图如下:[图片]也就是说一个Number类型的数字在内存中会被表示成:s x m x 2^e这样的格式。在ES规范中规定e的范围在-1074 ~ 971,而m最大能表示的最大数是52个1,最小能表示的是1,这里需要注意:知识点!!!二进制的第一位有效数字必定是1,因此这个1不会被存储,可以节省一个存储位,因此尾数部分可以存储的范围是1 ~ 2^(52+1)也就是说Number能表示的最大数字绝对值范围是 2^-1074 ~ 2^(53+971)精度丢失前面提到,计算机中存储小数是先转换成二进制进行存储的,我们来看一下0.1和0.2转换成二进制的结果:(0.1)10 => (00011001100110011001(1001)...)2(0.2)10 => (00110011001100110011(0011)...)2复制代码可以发现,0.1和0.2转成二进制之后都是一个无限循环的数,前面提到尾数位只能存储最多53位有效数字,这时候就必须来进行四舍五入了,而这个取舍的规则就是在IEEE 754中定义的,0.1最终能被存储的有效数字是0001(1001)(1001)(1001)(1001)(1001)(1001)(1001)(1001)(1001)(1001)(1001)(1001)101+(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)01=0100(1100)(1100)(1100)(1100)(1100)(1100)(1100)(1100)(1100)(1100)(1100)(1100)111复制代码这里注意,53位的存储位指的是能存53位有效数字,因此前置的0不算,要往后再取到53位有效数字为止。最终的这个二进制数转成十进制就是0.30000000000000004(不信的话可以找一个在线进制转换工具试一下。小结到此,这个精度丢失的问题已经解释清楚了,用一句话来概括就是,计算机中用二进制来存储小数,而大部分小数转成二进制之后都是无限循环的值,因此存在取舍问题,也就是精度丢失。最大安全整数这里直接推荐一篇文章,关于这个问题讲的非常清楚(文中有一处错误,会在下面指出。如果懒得看英文的话,可以看我的总结:最大安全整数9007199254740991对应的二进制数如图: [图片]53位有效数字都存储满了之后,想要表示更大的数字,就只能往指数数加一位,这时候尾数因为没有多余的存储空间,因此只能补0。如图所有,在指数位为53的情况下,最后一位尾数位为0的数字可以被精确表示,而最后一位尾数为为1的数字都不能被精确表示。也就是可以被精确表示和不能被精确表示的比例是1:1。同理,当指数为54的时候,只有最后两位尾数为00的可以被精确表示,也就是可以被精确表示和不能被精确表示的比例是1:3,当有效位数达到x(x>53)的时候,可以被精确表示和不能被精确表示的比例将是1 : 2^(x-53) - 1。可以预见的是,在指数越来越高的时候,这个指数会成指数增长,因此在Number.MAX_SAFE_INTEGER ~ Number.MAX_VALUE之间可以被精确表示的整数可以说是凤毛麟角。我发现这篇文章中的一个错误,文章中指出9007199254740998这个数字不能被精确表示,实际上是可以的,在指数位是53的情况下,偶数可以被精确表示,奇数不能被精确表示,不能被精确表示的最小偶数应该是当指数位为54,并且最后两位尾数为0的时候。小结之所以会有最大安全整数这个概念,本质上还是因为数字类型在计算机中的存储结构。在尾数位不够补零之后,只要是多余的尾数为1所对应的整数都不能被精确表示。总结可
你这题目,我真的不想看完。
– Shendu.CC 5年前这里不是写博客的地方,是提问题的地方。
– Shendu.CC 5年前