热点面试题:为什么 0.1+ 0.2 != 0.3,如何让其相等?
前言
欢迎关注 『前端进阶圈』 公众号 ,一起探索学习前端技术......
前端小菜鸡一枚,分享的文章纯属个人见解,若有不正确或可待讨论点可随意评论,与各位同学一起学习~
热点面试题:为什么 0.1+ 0.2 != 0.3,如何让其相等?
在开发过程中遇到类似这样的问题:
这里得到的不是想要的结果,要想等于 0.3,就要把它进行转化:
toFixed(num)
:方法可把 Number 四舍五入为指定小数位数的数字。
为什么会出现 0.1 + 0.2 != 0.3?
计算机是通过二进制的方式存储数据的,所以计算机计算 0.1 + 0.2 的时候,实际上是计算的两个数的二进制的和。0.1 的二进制是
0.0001100110011001100...
(1100 循环),0.2 的二进制是:0.00110011001100...
(1100 循环),这两个数的二进制都是无限循环的数。
JavaScript 是如何处理无限循环的二进制小数呢?
一般我们认为数字包括整数和小数,但是在 JavaScript 中只有一种数字类型:
Number
,它的实现遵循IEEE 754
标准,使用 64 位固定长度来表示,也就是标准的double 双精度浮点数
。在二进制科学表示法中,双精度浮点数的小数部分最多只能保留52
位,再加上前面的1
,其实就是保留53
位有效数字,剩余的需要舍去,遵从“0 舍 1 入
”的原则。根据这个原则,0.1 和 0.2 的二进制数相加,再转化为十进制数就是:
0.30000000000000004
。
双精度数是如何保存的?
第一部分(蓝色):用来存储符号位(sign),用来区分正负数,0 表示正数,占用 1 位
第二部分(绿色):用来存储指数(exponent),占用 11 位
第三部分(红色):用来存储小数(fraction),占用 52 位
对于 0.1,它的二进制为:
转为科学计数法(科学计数法的结果就是浮点数):
可以看出 0.1 的符号位为 0,指数位为-4,小数位为:
那么问题又来了,指数位是负数,该如何保存呢?
IEEE 标准规定了一个偏移量,对于指数部分,每次都加这个偏移量进行保存,这样即使指数是负数,那么加上这个偏移量也就是正数了。由于 JavaScript 的数字是双精度数,这里就以双精度数为例,它的指数部分为 11 位,能表示的范围就是 0~2047,IEEE 固定双精度数的偏移量为 1023。
当指数位不全是 0 也不全是 1 时(规格化的数值),IEEE 规定,阶码计算公式为 e-Bias。 此时 e 最小值是 1,则 1-1023= -1022,e 最大值是 2046,则 2046-1023=1023,可以看到,这种情况下取值范围是
-1022~1013
。当指数位全部是 0 的时候(非规格化的数值),IEEE 规定,阶码的计算公式为 1-Bias,即 1-1023= -1022。
当指数位全部是 1 的时候(特殊值),IEEE 规定这个浮点数可用来表示 3 个特殊值,分别是正无穷,负无穷,NaN。 具体的,小数位不为 0 的时候表示 NaN;小数位为 0 时,当符号位 s=0 时表示正无穷,s=1 时候表示负无穷。
对于上面的 0.1 的指数位为-4,-4+1023 = 1019 转化为二进制就是:
1111111011
.所以,0.1 表示为:
如何实现让其相等?
一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对 JavaScript 来说,这个值通常为 2-52,在 ES6 中,提供了
Number.EPSILON
属性,而它的值就是 2-52,只要判断0.1+0.2-0.3
是否小于Number.EPSILON
,如果小于,就可以判断为 0.1 + 0.2 === 0.3。
文章特殊字符描述:
问题标注
Q:(question)
答案标注
R:(result)
注意事项标准:
A:(attention matters)
详情描述标注:
D:(detail info)
总结标注:
S:(summary)
分析标注:
Ana:(analysis)
提示标注:
T:(tips)
往期回顾:
最后:
版权声明: 本文为 InfoQ 作者【控心つcrazy】的原创文章。
原文链接:【http://xie.infoq.cn/article/ddf05e3b2dca51e47737d556d】。文章转载请联系作者。
评论