五一宅家看书,所以接着更新一篇文章。

今天讲一下为什么03-0.2不等于0.1这个问题。

有点标题党的味道,在JavaScript中,当你试着对小数进行加减运算时,有时候会发现某个结果并非我们所想的那样,就比如标题中所说的为什么我用0.3去减0.2却得不到0.1?

当我碰到这个问题的时候我一下子也不知道问题到底出在哪,但他实实在在的就给出了一个0.09999999999999998的结论。

其实这个问题的本质原因就是在于计算机对浮点数的处理上,对于计算机的运算方法和数据的表示处理等内容是很大的一块内容,而且这块内容只要是计算机专业的同学在大学期间都已经接触到了而且很可能在你的期末考试试卷上就有这样的一道相关的题目是检测你这个知识点的。所以今天在朋友圈发了一条动态,内容是不好好学习的下场就是你连为什么03-0.2不等于0.1都不知道。

下面从浅显易懂的角度去讲述这个问题。

1.计算机中如何表示数据?

  计算机是个机器,内部所有数据和指令都是以01为基础来实现。也就是计算机的认知水平仅仅局限于二进制,而我们习惯于十进制计数规则,所以这中间就牵扯到一个各种数制之间的转换的问题。数制有二进制、八进制、十进制和十六进制,当然只要你高兴,搞个三进制五进制也不是不可以,但是这几个进制是我们约定俗成和被广泛使用的。

2.各数制间如何进行转换?

  真的不怎么想写这个问题,因为能回答这个问题的答案在你的课本里边早就有很详细且很全面的讲解,如果你浮躁到只想知道结果而自己不懂却连课本都不想翻的地步,那么这篇文章其实也没必要看了。

  但是为了与下面内容更好的连贯性,这里还是简单讲一下怎么从十进制转换到二进制的过程。

  十进制转换为二进制:

  比如11(10)这个十进制数,我们转换成二进制为11(10)=8+2+1=11=23+21+20=1000+10+1=1011(2);如果看不懂这个转换,那你去翻课本吧,这里就不列那些公式什么的了。 

  二进制转换为十进制:

  二进制转换为十进制也就是十进制转换为二进制的一个逆向过程,还是拿11(10)这个数字举例。1011(2)=1000+10+1=23+21+20=8+2+1=11(10)。

3.0.3-0.2不等于0.1和12条问题有什么关系?

  第一条说了因为我们习惯于非二进制,而计算机只能认知二进制,所以我们在编程语言中所写的那些以十进制表示数值的代码,对于底层的计算机实现来说是按照二进制来进行的。所以这其中肯定就会有一个进制转换的问题。

  当我们在高级语言里用十进制书写代码,到最后得到一个正确的依然是十进制的结果的这个过程,计算机在中间进行了两次数制转换,一次是十进制转二进制,转换完后进行数据操作,操作完成后所得到的二进制数据又逆向从二进制转换为十进制,最后呈现给我们一个我们所预期的结果。

  0.3-0.2不等于0.1的原因就出现在这个过程当中。

4.那么0.3-0.2不等于0.1到底是怎么形成的?

  紧接着上边的表述往下讲,以0.3-0.2为例,在JavaScript语言中完成这一个计算过程。

  首先我们打出0.3-0.2的表达式,本想我们很轻易的得出一个0.1的结果,然而,事实出乎意料,结果却给我们返回一个0.09999999999999998。

  数据在计算机中的分类大体为符号数和无符号数、定点数和浮点数,相互交叉便可以分为无符号定点数、浮点数和有符号定点数、浮点数。0.3(10)转换成二进制数据表示为:0.3(10)=2-3+2-4+2-7+2-8+......=0.001100110011001100110011001100110011001100110011001101(2),在JavaScript可以用对象的toSting方法查看,如0.3.toString(2);然后我们拿到这个转换后的二进制数据再按照单精度float或者双精度double的格式规格化成符号位阶数尾数的浮点数的表示形式,这就完成了从十进制到二进制表示的转换过程。

  再然后计算机就将0.3和0.2的二进制浮点数形式的表示进行运算,很显然,这个结果跟我们所想的0.3-0.2就该等于0.1可能会有些差异,因为我们在运算的过程中要对数据进行对阶操作,即两个数的阶数不同的话要调整到统一大小的阶数,调整过程中一般是右移,右移过程中就会出现精度受损的可能,因此计算完后的结果很有可能就不是我们所想要的那个结果了。

5.0.3-0.2就一定不等于0.1吗?

  首先,只要是小数,或者说是浮点运算,就有可能会出现精度受损的情况,但是只是可能,也不是说就一定会出现精度受损。具体要看操作数在进制转换过程中和二进制数值运算过程中有没有受到精度的影响,你比如0.5-0.25=2-1-2-2=2-2=0.25,这种情况下就不会出现精度受损,所以结果和我们预期的是一样的。

6.怎么规避这个问题?

  一切问题都可以解决,前提是我们知道了原因。现在我们知道了为什么0.3-0.2不等于0.1,那就反方向考虑解决问题的方法。那就是避免他们在数制转换过程和二进制数值运算过程中精度受损的影响。比如,我有3毛钱,也就是0.3元,然后买了只雪糕花掉我2毛钱,也就是0.2元,问还剩多少钱?如果是在以前,我们肯定不假思索的0.3-0.2=0.1想当然的就得出了结论。但是现在我们已经认识到这样会有问题,那么我们怎么避免他呢?那就是我为什么非要用元作单位而不用毛或者用分作单位?如果我用毛作单位3-2=1,用分作单位30-20=10就一定不会出现问题。因为整数的运算肯定不会有浮点运算的精度问题。所以这就是解决问题的思路。

完!

祝愉快。

深入理解JavaScript系列:为什么03-0.2不等于0.1的更多相关文章

  1. 深入理解JavaScript系列(33):设计模式之策略模式(转)

    介绍 策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户. 正文 在理解策略模式之前,我们先来一个例子,一般情况下,如果我们要做数据合法性验证,很 ...

  2. 深入理解JavaScript系列(44):设计模式之桥接模式

    介绍 桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化. 正文 桥接模式最常用在事件监控上,先看一段代码: addEvent(element, 'click', getBe ...

  3. 深入理解javascript系列(4):立即调用的函数表达式

    本文来自汤姆大叔 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行. 在详细了解这个之前,我们来谈了解一下“自执行”这个叫法,本文对这个功能的叫法 ...

  4. 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点

    深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点 2011-12-28 23:00 by 汤姆大叔, 139489 阅读, 119 评论, 收藏, 编辑 才华横溢的 ...

  5. [JS]深入理解JavaScript系列(4):立即调用的函数表达式

    转自:汤姆大叔的博客 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行.在详细了解这个之前,我们来谈了解一下"自执行"这个叫法 ...

  6. 深入理解JavaScript系列(49):Function模式(上篇)

    介绍 本篇主要是介绍Function方面使用的一些技巧(上篇),利用Function特性可以编写出很多非常有意思的代码,本篇主要包括:回调模式.配置对象.返回函数.分布程序.柯里化(Currying) ...

  7. 深入理解JavaScript系列(47):对象创建模式(上篇)

    介绍 本篇主要是介绍创建对象方面的模式,利用各种技巧可以极大地避免了错误或者可以编写出非常精简的代码. 模式1:命名空间(namespace) 命名空间可以减少全局命名所需的数量,避免命名冲突或过度. ...

  8. 深入理解JavaScript系列(48):对象创建模式(下篇)

    介绍 本篇主要是介绍创建对象方面的模式的下篇,利用各种技巧可以极大地避免了错误或者可以编写出非常精简的代码. 模式6:函数语法糖 函数语法糖是为一个对象快速添加方法(函数)的扩展,这个主要是利用pro ...

  9. 深入理解JavaScript系列(46):代码复用模式(推荐篇)

    介绍 本文介绍的四种代码复用模式都是最佳实践,推荐大家在编程的过程中使用. 模式1:原型继承 原型继承是让父对象作为子对象的原型,从而达到继承的目的: function object(o) { fun ...

随机推荐

  1. Python’s SQLAlchemy vs Other ORMs[转发 1]SQLObject

    SQLObject SQLObject is a Python ORM that maps objects between a SQL database and Python. It is becom ...

  2. 苹果下如果安装nginx,给nginx安装markdown第三方插件

    用brew install nginx 这样安装的是最新版的nginx, 但是在有些情况下,安装第三方插件需要特定的版本,更高一级的版本可能装不上. 它的原理是下载安装包进行自动安装,建立软链,这样就 ...

  3. 节约内存:Instagram的Redis实践(转)

    一.问题:     数据库表数据量极大(千万条),要求让服务器更加快速地响应用户的需求. 二.解决方案:      1.通过高速服务器Cache缓存数据库数据      2.内存数据库 三.主流解Ca ...

  4. Fake chat script for website download

    Are you searching for free fake webchat script then you are at the right place go get download your ...

  5. erlang服务器启动,有情况会报,enif_send: env==NULL no ono-SMP VMAborted 的错误报告?

    问题的原因所在: 1:因为你当前使用的主机是一个单核的主机(不会自动启动): 2:多核上如果不设置-smp enable是不会有什么问题的,因为从OTP R12B开始,如果操作系统报告有多于1个的CP ...

  6. ionic环境搭建及新建项目中的各种问题

    具体流程可见http://bbs.ionic-china.com/read-7.html 问题1.安装ionic cordova失败 解决方法:修改npm的源,npm config set regis ...

  7. AndroidManifest.xml file missing!

    1.点击菜单栏中的Project——>Clean一下. 2.把AndroidManifest.xml文件再保存一下就可以了.

  8. replace

    replace方法的语法是:stringObj.replace(rgExp, replaceText) 其中stringObj是字符串(string),reExp可以是正则表达式对象(RegExp)也 ...

  9. 腾讯云服务器centos 6.5(jdk+tomcat+vsftp)、腾讯mysql数据库 及 tomcat自启动 配置教程

    1.腾讯云数据库配置 1.考虑到安全性问题,,平常不使用root用户登录,新增一个用户名neil,用来管理项目的数据库 a.首先登录root创建db_AA数据库 b.在root用户下,创建neil用户 ...

  10. Secondary NameNode:的作用?

    前言 最近刚接触Hadoop, 一直没有弄明白NameNode和Secondary NameNode的区别和关系.很多人都认为,Secondary NameNode是NameNode的备份,是为了防止 ...