为什么JS中0.1+0.2 != 0.3

在我曾经的一篇《 javascript入门教程 (2) 》中,讲到JS中数字运算时,我们提到过一个叫做 数字运算中的精度缺失的问题,当时我们只是简单说了下,并未对其原因做了解。这篇文章,我就带着大家了解下 JS运算中精度的缺失问题

首先我们先来看一个例子

console.log(0.1 + 0.2)  // 结果是0.30000000000000004,而不是3

这里0.1 + 0.2 != 0.3 这个就是我们要解决的问题了。

要弄清这个问题的原因,首先我们需要了解下在计算机中数字是如何存储和运算的。在计算机中,数字无论是定点数还是浮点数都是以多位二进制的方式进行存储的。在JS中数字采用的IEEE 754的双精度标准进行存储,我们可以无需知道他的存储形式,只需要简单的理解成就是存储一个数值所使用的二进制位数比较多而已,这样得到的数会更加精确。

这里为了简单直观,我们使用定点数来说明问题。在定点数中,如果我们以8位二进制来存储数字。

对于整数来说,十进制的35会被存储为: 00100011 其代表 2^5 + 2^1 + 2^0
对于纯小数来说,十进制的0.375会被存储为: 0.011 其代表 1/2^2 + 1/2^3 = 1/4 + 1/8 = 0.375

而对于像0.1这样的数值用二进制表示你就会发现无法整除,最后算下来会是 0.000110011....由于存储空间有限,最后计算机会舍弃后面的数值,所以我们最后就只能得到一个近似值。在JS中采用的IEEE 754的双精度标准也是一样的道理,我们且不管这个标准下的存储方式跟定点数存储有何不同,单单在这一点上他们都是相同的,也就是存储空间有限,当出现这种无法整除的小数的时候就会取一个近似值,在js中如果这个近似值足够近似,那么js就会认为他就是那个值。(比较拗口,举个例子)

console.log(0.1000000000000001)
// 0.1000000000000001 (中间14个0,会打印出它本身) console.log(0.10000000000000001)
// 0.1 (中间15个0,js会认为这两个值足够接近,所以会显示0.1)

所以我们现在应该可以理解,就是说由于0.1转换成二进制时是无限循环的,所以在计算机中0.1只能存储成一个近似值。另外说一句,除了那些能表示成 x/2^n 的数可以被精确表示以外,其余小数都是以近似值得方式存在的。
    在0.1 + 0.2这个式子中,0.1和0.2都是近似表示的,在他们相加的时候,两个近似值进行了计算,导致最后得到的值是0.30000000000000004,此时对于JS来说,其不够近似于0.3,于是就出现了0.1 + 0.2 != 0.3 这个现象。
    当然,也并非所有的近似值相加都得不到正确的结果。有时两个近似值进行计算的时候,得到的值是在JS的近似范围内的,于是就可以得到正确答案。至于哪些值计算后能得到正确结果,哪些不能,我们也不需要去记。最好的方法就是我们想办法规避掉这类小数计算时的精度问题就好了。

那么最常用的方法就是将浮点数转化成整数计算。因为整数都是可以精确表示的。

方法也很简单,举个例子:

对于0.1 + 0.02 我们需要转化成 ( 10 + 2 ) / 1e2
对于0.1 * 0.02 我们则转化成 1 * 2 / 1e3

按照这个思路,写个简单的方法就好了。

另外就是如果你在学习前端的过程中有任何问题想要咨询,欢迎关注 LearnInPro的公众号,在上面随时向我提问哦。

为什么JS中0.1+0.2 != 0.3的更多相关文章

  1. 在js中做数字字符串加0补位,效率分析

    分类: Jquery/YUI/ExtJs 2010-08-30 11:27 2700人阅读 评论(0) 收藏 举报 functiondate算法语言c 通常遇到的一个问题是日期的“1976-02-03 ...

  2. 在js中做数字字符串补0

    转自(http://blog.csdn.net/aimingoo/article/details/4492592) 通常遇到的一个问题是日期的“1976-02-03 HH:mm:ss”这种格式 ,我的 ...

  3. js中setTimeout() 时间参数为0

    当看到下面 这种setTimeout 设置为0 写法的时候一脸懵逼,完全没用过. var fuc = [1,2,3]; for(var i in fuc){ setTimeout(function() ...

  4. java、js中实现无限层级的树形结构(类似递归)

    js中: var zNodes=[ {id:0,pId:-1,name:"Aaaa"}, {id:1,pId:0,name:"A"}, {id:11,pId:1 ...

  5. js中要声明变量吗?

    你好,js语言是弱类型语言,无需申明即可直接使用,默认是作为全局变量使用的.建议:在function里时应使用var 申明变量,这样改变量仅仅只在function的生存周期内存在,不会污染到,全局控件 ...

  6. 在JS中关于堆与栈的认识function abc(a){ a=100; } function abc2(arr){ arr[0]=0; }

    平常我们的印象中堆与栈就是两种数据结构,栈就是先进后出:堆就是先进先出.下面我就常见的例子做分析: main.cpp int a = 0; 全局初始化区 char *p1; 全局未初始化区 main( ...

  7. 字符串0.在php和js中转换为布尔类型 值是false还是true

    在php 中 $a = '0'; $b = (bool)$a; var_dump($a);//输出false 在js中官方说明: Note:If the value parameter is omit ...

  8. js中的0就是false,非0就是true及案例

    在处理js代码判断真假时经常会这么写. 但fun()可能得到的是数字0,这可不是表示的没有值,但是!js中的数字0就是false,非0就是true. 于是0就被无情的当做false了. 已经被这个坑过 ...

  9. 深入理解 Node.js 中 EventEmitter源码分析(3.0.0版本)

    events模块对外提供了一个 EventEmitter 对象,即:events.EventEmitter. EventEmitter 是NodeJS的核心模块events中的类,用于对NodeJS中 ...

  10. 为什么js中要用void 0 代替undefined

    这个是Backbone.js中的一句源码 if (callback !== void 0 && 'context' in opts && opts.context == ...

随机推荐

  1. 【转】合理的布局,绚丽的样式,谈谈Winform程序的界面设计

    从事Winform开发很多年了,由于项目的需要,设计过各种各样的界面效果.一般来说,运用传统的界面控件元素,合理设计布局,能够设计出比较中规中矩的标准界面:利用一些换肤的控件或者部分界面组件,能够设计 ...

  2. ubuntu上安装R的时候遇到的问题总结

    首先感谢这两篇博客的指导,第一篇是关于报错的总结,第二篇是第一篇中没有提到的错误,也就是我在安装的时候出现的错误. 1.下载R包 (去官网选择一个离你最近的镜像网址,我的是清华提供的镜像下载速度比较快 ...

  3. Postgresql 连接更新

    update dbo.m_role_fun a set role_code = b.rsc from (select rsc, fun_code from dbo.m_fun) b where a.f ...

  4. 从0开始整合SSM框架--2.spring整合mybatis

    依赖:<properties> <!-- spring版本号 --> <spring.version>4.1.3.RELEASE</spring.versio ...

  5. nohup磁盘打满问题排查与解决

    使用nohup ... & 命令启动服务器后,磁盘满了,服务宕了,然后一步一步排查是哪个文件过大,最终定位到是nohup.out文件过大,占了40G, df -lh #磁盘容量命令 du -s ...

  6. github提交代码不用输入账号密码的解决方案

    1.在命令行输入命令: git config --global credential.helper store 这一步会在用户目录下的.gitconfig文件最后添加: [credential] he ...

  7. 微信小程序通过CODE换取session_key和openid

    微信小程序的用户信息获取需要请求微信的服务器,通过小程序提供的API在小程序端获取CODE,然后将CODE传入到我们自己的服务器,用我们的服务器来换取session_key和openid. 小程序端比 ...

  8. 中南月赛 1313: ZZY的宠物

    1313: ZZY的宠物 Time Limit: 2 Sec  Memory Limit: 128 MBSubmit: 114  Solved: 59[Submit][Status][Web Boar ...

  9. windows操作系统用命令提示符查看占用端口号的进程

    在开发中有时我们需要确定哪个占用了8080端口,在windows命令行窗口下执行: 命令执行后打印出来的结果如下所示:

  10. 关于 webpack 跨域

    一.使用  http-proxy-middleware  代理 安装 http-proxy-middleware 依赖 在src 目录下 新建一个 setupProxy.js文件 // 引用依赖 va ...