深入理解JavaScript系列:为什么03-0.2不等于0.1
五一宅家看书,所以接着更新一篇文章。
今天讲一下为什么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的更多相关文章
- 深入理解JavaScript系列(33):设计模式之策略模式(转)
介绍 策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户. 正文 在理解策略模式之前,我们先来一个例子,一般情况下,如果我们要做数据合法性验证,很 ...
- 深入理解JavaScript系列(44):设计模式之桥接模式
介绍 桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化. 正文 桥接模式最常用在事件监控上,先看一段代码: addEvent(element, 'click', getBe ...
- 深入理解javascript系列(4):立即调用的函数表达式
本文来自汤姆大叔 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行. 在详细了解这个之前,我们来谈了解一下“自执行”这个叫法,本文对这个功能的叫法 ...
- 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点
深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点 2011-12-28 23:00 by 汤姆大叔, 139489 阅读, 119 评论, 收藏, 编辑 才华横溢的 ...
- [JS]深入理解JavaScript系列(4):立即调用的函数表达式
转自:汤姆大叔的博客 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行.在详细了解这个之前,我们来谈了解一下"自执行"这个叫法 ...
- 深入理解JavaScript系列(49):Function模式(上篇)
介绍 本篇主要是介绍Function方面使用的一些技巧(上篇),利用Function特性可以编写出很多非常有意思的代码,本篇主要包括:回调模式.配置对象.返回函数.分布程序.柯里化(Currying) ...
- 深入理解JavaScript系列(47):对象创建模式(上篇)
介绍 本篇主要是介绍创建对象方面的模式,利用各种技巧可以极大地避免了错误或者可以编写出非常精简的代码. 模式1:命名空间(namespace) 命名空间可以减少全局命名所需的数量,避免命名冲突或过度. ...
- 深入理解JavaScript系列(48):对象创建模式(下篇)
介绍 本篇主要是介绍创建对象方面的模式的下篇,利用各种技巧可以极大地避免了错误或者可以编写出非常精简的代码. 模式6:函数语法糖 函数语法糖是为一个对象快速添加方法(函数)的扩展,这个主要是利用pro ...
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
介绍 本文介绍的四种代码复用模式都是最佳实践,推荐大家在编程的过程中使用. 模式1:原型继承 原型继承是让父对象作为子对象的原型,从而达到继承的目的: function object(o) { fun ...
随机推荐
- tensorflow版的bvlc模型
研究相关的图片分类,偶然看到bvlc模型,但是没有tensorflow版本的,所以将caffe版本的改成了tensorflow的: 关于模型这个图: 下面贴出通用模板: from __future__ ...
- Yii读取TXT文件数据插入到数据库
个人平时会用到的方法,记录一下并分享给需要的朋友,Yii批量添加还需要clone一下model才可以,不然只会插入一条数据. 也可以把文件通过参数的方式调入到方法中. // 读取CVS文件 funct ...
- Unity3D Layout 快捷键
我的需求是开发的时候一种布局,运行的时候一种布局,Unity3D 选项中的自定义快捷键的太少,只能另想办法.Google 之后,找到解决方法:Editor layout hotkeys? 1.创建菜单 ...
- 深入springMVC------文件上传源码解析(上篇)
最近在项目中,使用springmvc 进行上传文件时,出现了一个问题: org.springframework.web.multipart.MultipartException: The curren ...
- Android 判断字符串是否为空
TextUtils.isEmpty(str) 可以判断字符串是否为null或者"",当是的时候为true,否的时候为false
- mysql 删除重复记录语句
mysql 根据条件删除重复记录 只保留最小id的重复数据 DELETEFROM newsWHERE news_id IN ( SELECT a.news_id FROM ( SELECT news_ ...
- -bash: msgunfmt: command not found
执行命令:msgunfmt frontend.mo -o frontend.po 解决方法: 安装gettext完成后如下:
- Bootstrap<基础十三> 按钮组
按钮组允许多个按钮被堆叠在同一行上.当你想要把按钮对齐在一起时,这就显得非常有用.你可以通过Bootstrap 按钮(Button) 插件 添加可选的 JavaScript 单选框和复选框样式行为. ...
- curl post
//Post方式实现 $url = "http://localhost/web_services.php"; $post_data = array ("username& ...
- iOS -- 处理推送消息
简介 很多应用都会实现推送功能,我们可以集成第三方框架实现推送功能,比如: JPush推送:https://www.jpush.cn 个推:http://www.getui.com 下面来说说收到推送 ...