JavaScript浮点运算0.2+0.1 !== 0.3

浮点运算JavaScript

本文主要讨论JavaScript的浮点运算,主要包括

  • JavaScript number基本类型

  • 二进制表示十进制

  • 浮点数的精度

number 数字类型

在JavaScript中,数字只有number这一种类型;

var intS = 2,floatA = 0.1;
typeof intS;   // number
typeof floatA; //number

那么这个情况下应该很容易理解一件事情:number应该是实现的浮点型数来标识所有的数;
而实际上也是这样;JavaScript的number类型按照ECMA的JavaScript标准,它的Number类型就是IEEE 754的双精度数值,相当于java的double类型。IEEE 754标准《二进制浮点数算法》(www.ieee.org)就是一个对实数进行计算机编码的标准。

十进制转换为二进制

同样,在计算机的世界里,应该是只有二进制数据的,不是0就是1,那么为了表达生活中最为常见的十进制数据,就会有个转换过程;这个就是十进制转换为二进制的方法;
参考:http://www.cnblogs.com/xkfz00...

十进制整数转换为二进制

这个情况比较常见:3 =》 01;5 =》101;十进制整数转换为二进制整数采用"除2取余,逆序排列"法。具体做法是:用2去除十进制整数,可以得到一个商和余数;再用2去除商,又会得到一个商和余数,如此进行,直到商为零时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列起来
换算的法则是,使用一个十进制数字来示例: 173 =》 10101101:
图片描述

十进制小数变为二进制

十进制的小数转换为二进制,0.5 =》 0.1 ;十进制小数转换成二进制小数采用"乘2取整,顺序排列"法。具体做法是:用2乘十进制小数,可以得到积,将积的整数部分取出,再用2乘余下的小数 部分,又得到一个积,再将积的整数部分取出,如此进行,直到积中的小数部分为零,或者达到所要求的精度为止。然后把取出的整数部分按顺序排列起来,先取的整数作为二进制小数的高位有效位,后取的整数作为低位有效位。
示例 0.8125 =》 0.1101
图片描述

完整的十进制小数转为二进制

从上面的讲述中可以知道,一个十进制的小数:173.8125 转换为二进制是 10101101.1101;在计算机中一般都会使用科学计算来处理浮点数,也就是 173.8125 == 1.738125 * 10(2);那么二进制的表示也不例外,通过指数来定位小数点,用固定的精度来表示数据;

在JavaScript使用的IEEE 754的双精度数值,一个JavaScript的number表示应该是二进制如下格式:

 1[-/+] 11[位指数]        52[数值]                 64位长
+  -  + -------- + ----------------------- +

64位的具体表述在不同系统可能顺序会有差异,但是都是包含以下三部分:

  1. 符号位: 1bit,0表示正数,1表示负数

  2. 指数位:11bit,也就是需要移动的位数,也就是指数的大小;由于会存在负数和证书,所以这里用了一个偏移的方式处理,也就是真正的指数+1023,这样的话就表示了【-1023 ~ 1024】;而-1023也就是全0,1024就是全1;

  3. 尾数:52bit,这里需要注意的是由于小数点前面以为必须为1,所以实际上是52+1=53位;

参考:http://coolcao.com/2016/10/12...
http://www.cnblogs.com/kingwo...
可以看到,由于二进制的精确位数只有52+1位,那么类似 1/3 这样的无理数,那么肯定是无法表示的,而且二进制还有很多有理数 0.1这样的也无法在52位精度的范围内表示精确无误;都会被截取53位以后的所有数字。

0.1+0.2 !== 0.3 [true]

有了以上的铺垫,那么我们很容易就可以推到出原因了;推理步骤如下:

十进制0.1 =》 [利用上面说的方法来转换,乘以2取整数,然后顺序获取取出得数]

 =>二进制为:0.0001100110011[0011…](循环0011,无限循环)   =>指数表示:尾数为1.1001100110011001100…1100(共52位,除了小数点左边的必须为1的数据),指数为-4(-4+1023 = 1019 二进制移码为 01111111011),符号位为0  => 计算机存储为:0 01111111011 10011001100110011…11001  => 因为尾数最多52位,所以实际存储的值为0.00011001100110011001100110011001100110011001100110011001  

而十进制0.2

 => 二进制0.0011001100110011…(循环0011)  =>尾数为1.1001100110011001100…1100(共52位,除了小数点左边的1),指数为-3(-3+1023=1020二进制移码为01111111100),符号位为0  => 存储为:0 01111111100 10011001100110011…11001  因为尾数最多52位,所以实际存储的值为0.00110011001100110011001100110011001100110011001100110011  

 那么两者相加得:
加法运算的时候需要注意以下几点:

  • 对阶:需要将指数小的,变得和指数大的一样,通过位数移位【移位注意有一个隐藏的小数点左边的固定的1】

  • 尾数运算:加法运算

  • 结果规格化:规范为 位数的左边第一位必须为隐藏的1,

  • 舍入处理:主要是在截取的时候进行的处理,最后位舍去时为0直接舍去,为1则+1;【有多种舍入处理】

  • 溢出判断:

尾数加法运算开始,注意小数点左边隐藏的默认1

   [1].1001100110011001100110011001100110011001100110011001+ [1].1001100110011001100110011001100110011001100110011001

//由于0.1是-3阶,指数是-4,而0.2的指数位-3,故而取大者-3;这样0.1需要右移一位,刚好之前小数点左侧隐藏的1被移出来了;如下

      .1100110011001100110011001100110011001100110011001100 【1被舍去】
+  [1].1001100110011001100110011001100110011001100110011001
=   100110011001100110011001100110011001100110011001100111

此时阶码变为了 -3,但是由于进位了两位,但是最高位需要保留,故而阶位只是+1,也就是-2了.也就是01111111101,
进行舍入处理,由于最高位一定是1,所以对结果最高位去除,末尾一位去除,由于是1,故而+1处理,得到新的52位位数为:

 新的尾数: 0011001100110011001100110011001100110011001100110100
存储为: 0  01111111101  0011001100110011001100110011001100110011001100110100
十进制就是:0.3000000000000000444089209850062616169452667236328125
截取为:   0.30000000000000004  

转换成10进制之后得到:0.30000000000000004

思考

看到 0.1+0.2 = 0.30000000000000004;我开始慌了,那么0.1+0.3 === 0.4 对吗?我也不知道,虽然最后运算的时候证明是对的,但是还是可以按照我们的方法进行分析

 十进制0.1  [利用上面说的方法来转换,乘以2取整数,然后顺序获取取出得数]=>二进制为:0.0001100110011[0011…](循环0011,无限循环)   =>指数表示:尾数为1.1001100110011001100…1100(共52位,除了小数点左边的必须为1的数据),指数为-4(-4+1023 = 1019 二进制移码为 01111111011),符号位为0  => 计算机存储为:0 01111111011 10011001100110011…11001  => 因为尾数最多52位,所以实际存储的值为0.00011001100110011001100110011001100110011001100110011001 而十进制0.3  => 二进制0.010011001100110011001100110011001...(循环1001)  =>尾数为1.00110011001100110011…0011(共52位,除了小数点左边的1),指数为-2(-2+1023=1021二进制移码为01111111101),符号位为0  => 存储为:0 01111111101 0011001100110011…110011  因为尾数最多52位,所以实际存储的值为0.01001100110011001100110011001100110011001100110011001100  那么两者相加得[对阶,为大者-2,-4阶数的0.1左移两位]:      .0110011001100110011001100110011001100110011001100110
+ [1].0011001100110011001100110011001100110011001100110011 
=   1.1001100110011001100110011001100110011001100110011001新的尾数: 1001100110011001100110011001100110011001100110011001
存储为: 0  01111111101  1001100110011001100110011001100110011001100110011001
十进制就是:0.39999999999999996447286321199499070644378662109375
截取为:   0.4 

可以看到,JavaScript的小数保留了17位,

//一个52位小数的最小二进制的表示
0.0000000000000000000000000000000000000000000000000001
0.0000000000000002220446049250313 
//一个53【加头部默认1位】位小数的最小二进制数
0.00000000000000000000000000000000000000000000000000001
0.00000000000000011102230246251565
Math.pow(2, 53)
9007199254740992 //当大于这个数的时候就会丢失精度
Math.pow(2, -53)
1.1102230246251565e-16  //当小于这个数也会丢失精度

JavaScript采用了17位来默认截取数据,根据四舍五入方法或者是说二进制中的0舎1进位的方式截取。
所以这样的加法有的时候会出现精度问题,有的又不会。看看具体的情况,在chrome的console里面运行的结果如下:

0.4-0.1
0.300000000000000040.3+0.1
0.40.1+0.2
0.30000000000000004

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/394636.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html获取data-*值,html5 获取和设置data-*属性值的四种方法讲解

1、获取id的对象2、需要获取的就是data-id 和 dtat-vice-id的值一:getAttribute()方法const getId document.getElementById(getId);// //getAttribute()取值属性console.log(getId.getAttribute("data-id"));//console.log(getId.getAttribute("da…

三菱模拟量输入与输出程序_初学PLC是学习西门子还是三菱?

PLC的种类繁多,品牌大多分为欧系、日系、美系。德系PLC以西门子为主,日系有三菱、欧姆龙、松下……,美系有罗克韦尔(A-B)通用电气(GE)公司、莫迪(MODICON)公司等。美国和欧洲的PLC技术是在相互隔离情况下独立研究开发的,因此美国和…

性能测试十四:Xshell链接linux虚拟机

一、先装一个linux虚拟机 VBoxcentos1、先下载Linux镜像文件的ovf或者OVA文件2、打开vbox,点击菜单栏“管理”-“导入虚拟电脑3、选择解压路径中的ovf或者OVA文件,点击下一步 4、点击“导入”,等待完成5、导入成功后,选择新导入的…

代码编写工具_我希望在开始编写代码时就已经知道的工具:已复习

代码编写工具by Mario Hoyos通过马里奥霍约斯(Mario Hoyos) 我希望在开始编写代码时就已经知道的工具:已复习 (Tools I wish I had known about when I started coding: Revisited) A few days ago, I wrote this article for freeCodeCamp which has since gone o…

Hydra扫描姿势

参数详解: -R 根据上一次进度继续破解 -S 使用SSL协议连接 -s 指定端口 -l 指定用户名 -L 指定用户名字典(文件) -p 指定密码破解 -P 指定密码字典(文件) -e 空密码探测和指定用户密码探测(ns) -C 用户名可以用:分割(username:password)可以代替-l username -p pass…

html5进度条插件 传递参数,Html5进度条插件(自写)

(function () {window.H5ProgressBar function (obj) {this.height obj.height;this.width obj.width;this.speed obj.speed;};//在界面上布局元素H5ProgressBar.prototype.drawLayout function () {document.write("开始下载")document.write(" ")do…

python合并txt文本_Python实现将目录中TXT合并成一个大TXT文件的方法

本文实例讲述了Python实现将目录中TXT合并成一个大TXT文件的方法。分享给大家供大家参考。具体如下: 在网上下了一个dota的英雄攻略,TXT格式,每个英雄一个文件,看得疼,就写了一个小东西,合并一下. #codinggbk import os import sys import glob def dirTxtToLargeTx…

读取字符串中的数字和小数

package com.jm.label.tools;import java.util.Map;import java.util.TreeMap;import java.util.regex.Matcher;import java.util.regex.Pattern;/** * 读取字符串中的数字和小数 * author JM.H * */public class DigitUtil { public static String getNumber(String str){ …

html网页设计要点,网站交互设计的8个要点

一、力求一致性例如网站首页需要和每一个下级页面保持一致的风格,导航都要放在屏幕的左上角,具有高度一致性的界面能给人清晰整洁的感觉。二、允许频繁使用快捷键快捷键表示产品使用的灵活性和有效性,想想每次我们使用搜索引擎的时候是鼠标点…

行为扩展以及插件机制

在thinkPHP中的行为扩展和插件机制。 首先行为扩展这个概念是TP框架的核心组成之一,关于行为的解释我就粗略的概括一下吧: TP在从接受到HTTP请求到最终将试图输出,期间经历的很多步骤,这些步骤大家可以在http://document.thinkphp…

python android 库_Python库

Gevent Gevent是一个基于greenlet的Python的并发框架,以微线程greenlet为核心,使用了epoll事件监听机制以及诸多其他优化而变得高效。 于greenlet、eventlet相比,性能略低,但是它封装的API非常完善,最赞的是提供了一个…

ios 应用商店_如何在预算范围内制作值得应用商店使用的iOS应用预览

ios 应用商店Back in 2014, Apple made it possible to add an app preview to the the App Store. App previews are the best way to show potential users what your app has to offer before they download the app. In fact, users are 3x more likely to install an app …

搭建nfs共享存储服务之二nfs服务端配置

1.1.NFS服务端配置文件路径为: /etc/exports,并且默认为空,需要用户自行配置。/etc/exports文件配置格式为:NFS共享的目录 NFS客户端地址1(参数1,参数2...)客户端地址2(参数1&#x…

计算机word基本知识选择题,2017计算机基础考试选择题「附答案」

2017计算机基础考试选择题「附答案」一、单项选择题(每题1.5分,共30分)1、文件名使用通配符的作用是(b)A、减少文件名所占用的磁盘空间B、便于一次处理多个文件C、便于给一个文件命名D、便于保存文件2、操作系统是一种(a)A、系统软件 B、系统程序库 C、编译程序系统…

[JLOI2015]管道连接(斯坦纳树)

[Luogu3264] 原题解 多个频道,每个频道的关键点要求相互联通 详见代码,非常巧妙 #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<queue> #define debug(...) fprintf(stderr,__VA_ARGS__) #define Debug(…

关于web前端的学习路线

第一阶段&#xff1a; HTMLCSS:HTML进阶、CSS进阶、divcss布局、HTMLcss整站开发、 JavaScript基础&#xff1a;Js基础教程、js内置对象常用方法、常见DOM树操作大全、ECMAscript、DOM、BOM、定时器和焦点图。 JS基本特效&#xff1a;常见特效、例如&#xff1a;tab、导航、整页…

值大于为此列指定的允许精度_电能质量测试精度会受到哪些因素影响?如何解决?...

关于电能质量&#xff08;也称为PQ:Power Quality&#xff09;研究的主题已成为多方面的话题。其需要考虑的不仅仅是IEC 61000-x-x电磁兼容性标准中规定的实际电能质量现象。在实践中&#xff0c;通常还会增加其他重要参数来保证供电的安全性&#xff0c;在某些情况下这些参数甚…

SEO博客

http://www.chinamyhosting.com/seoblog/分类: SEO 本文转自快乐就好博客园博客&#xff0c;原文链接&#xff1a;http://www.cnblogs.com/happyday56/archive/2008/05/10/1191435.html&#xff0c;如需转载请自行联系原作者

gis计算各省河流长度_用河流和各方解释安全漏洞

gis计算各省河流长度by Andrea Zanin由Andrea Zanin 用河流和各方解释安全漏洞 (Security Vulnerabilities Explained with Rivers and Parties) Security vulnerabilities can be boring to learn. But you still need to learn them, unless you want some hacker to delete…

Delphi关于记录文件的操作

http://www.cnblogs.com/railgunman/archive/2010/08/16/1801004.html Delphi关于记录文件的操作 本例子几个变量的说明TFileRec record   //记录定义Day : Integer;...          //其他定义end;f : File of TFileRec;   //标准的输入/输出文件FilRec : TFileR…