热点面试题:为什么 0.1+ 0.2 != 0.3,如何让其相等?

简介: 热点面试题:为什么 0.1+ 0.2 != 0.3,如何让其相等?

热点面试题:为什么 0.1+ 0.2 != 0.3,如何让其相等?

  • • 在开发过程中遇到类似这样的问题:
let n1 = 0.1,
    n2 = 0.2;
console.log(n1 + n2 === 0.3); // false -> 0.1 + 0.2 = 0.30000000000000004
  • • 这里得到的不是想要的结果,要想等于 0.3,就要把它进行转化:
(n1 + n2).toFixed(2); // 注意,toFixed为四舍五入

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.00011001100110011001100110011001100110011001100110011001 10011...
  • • 转为科学计数法(科学计数法的结果就是浮点数):
(1.1001100110011001100110011001100110011001100110011001 * 2) ^ -4;
  • • 可以看出 0.1 的符号位为 0,指数位为-4,小数位为:
1001100110011001100110011001100110011001100110011001;
  • • 那么问题又来了,指数位是负数,该如何保存呢?
  • • 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 表示为:
0 1111111011 1001100110011001100110011001100110011001100110011001

如何实现让其相等?

  • • 一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对 JavaScript 来说,这个值通常为 2-52,在 ES6 中,提供了Number.EPSILON属性,而它的值就是 2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为 0.1 + 0.2 === 0.3。
function numberepsilon(arg1, arg2) {
    return Math.abs(arg1 - arg2) < Number.EPSILON;
}
console.log(numberepsilon(0.1 + 0.2, 0.3)); // true

文章特殊字符描述

问题标注 Q:(question)答案标注 R:(result)注意事项标准:A:(attention matters)详情描述标注:D:(detail info)总结标注:S:(summary)分析标注:Ana:(analysis)提示标注:T:(tips)

相关文章
|
1月前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
6月前
|
存储 算法 NoSQL
百度面试:如何用Redis实现限流?
百度面试:如何用Redis实现限流?
77 2
|
存储 设计模式 缓存
缓解缓存击穿的大杀器之---singleflight深入浅出
缓解缓存击穿的大杀器之---singleflight深入浅出
505 0
缓解缓存击穿的大杀器之---singleflight深入浅出
|
算法 Java API
2023年春招热点面试题(一)------新特性
2023年春招热点面试题(一)------新特性
76 0
面试官:谈关于缓存穿透+击穿+雪崩,热点数据失效问题的解决方案
当我们查询一条数据时,先去查询缓存,如果缓存有就直接返回,如果没有就去查询数据库,然后返回。这种情况下就可能出现下面的一些现象。 2.缓存穿透
|
缓存 JSON NoSQL
阿里面试官:给我描述一下缓存击穿的现象,并说说你的解决思路?
缓存(内存 or Memcached or Redis.....)在互联网项目中广泛应用,本篇博客将讨论下缓存击穿这一个话题,涵盖缓存击穿的现象、解决的思路、以及通过代码抽象方式来处理缓存击穿。
|
存储 缓存 NoSQL
Redis缓存穿透和雪崩相关概念(面试高频,工作常用)
Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面,但同时,它也带来了一些问题,其中,最重要的问题,就是数据的一致性问题。从严格意义上讲,这个无解。如果对数据的一致性要求很高,那么就不能使用缓存。
158 0
Redis缓存穿透和雪崩相关概念(面试高频,工作常用)
|
存储 缓存 NoSQL
【Redis实战】面试热点:缓存穿透、缓存击穿、缓存雪崩的区别和解决方案
面试热点:缓存穿透、缓存击穿、缓存雪崩的区别和解决方案
【Redis实战】面试热点:缓存穿透、缓存击穿、缓存雪崩的区别和解决方案