0.1 + 0.2不等于0.3?为什么JavaScript有这种“骚”操作_js 为什么x=0.1 可以得到0.1-程序员宅基地

技术标签: js  

写在前面

随着消费观念的改变,线上消费已经成为大众生活中不可或缺的一部分。在保证消费安全和用户隐私的同时,精准度也是必不可少的一环。试想一下,用户在一款产品上消费,结算金额出错,用户会怎么想?(数体教 or WTF?),妥妥的差评了吧。 这样不要说用户粘性了,留存都是问题。当Boss得知用户的遭遇后,估计贡献代码的同志会成为前员工或者大家口中的已故员工某某某。作为一个优秀(laji)的程序员,好久之前就遇到过精确计算的问题,但是偷懒并没有整理出来,直到最近有人问我相关问题,突然觉得有必要写写我对js精确计算的理解

JavaScript中计算的翻车现场

言归正传 书接上文,先来一个简单(landajie)的?,展示一下js计算的常规操作

 

这种送分题,js却送了命。令人窒息的操作。这个例子很常见,我们不是为了关注这个例子本身,我们需要明白的是为什么会出现这样的结果?哪一步出了问题?还有那些计算可能会出现这样的问题?怎么解决?

 

JavaScript是如何表示数字的?

JavaScript使用Number类型表示数字(整数和浮点数),遵循 IEEE 754 标准 通过64位来表示一个数字

通过图片具体看一下数字在内存中的表示

 

 

图片文字说明

 

  • 第0位:符号位,0表示正数,1表示负数(s)
  • 第1位到第11位:储存指数部分(e)
  • 第12位到第63位:储存小数部分(即有效数字)f

既然说到这里,再给大家科普一个小知识点:js最大安全数是 Number.MAX_SAFE_INTEGER == Math.pow(2,53) - 1, 而不是Math.pow(2,52) - 1, why?尾数部分不是只有52位吗?

这是因为二进制表示有效数字总是1.xx…xx的形式,尾数部分f在规约形式下第一位默认为1(省略不写,xx..xx为尾数部分f,最长52位)。因此,JavaScript提供的有效数字最长为53个二进制位(64位浮点的后52位+被省略的1位)

简单验证一下

 

 

运算时发生了什么?

首先,计算机无法直接对十进制的数字进行运算,这是硬件物理特性已经决定的。这样运算就分成了两个部分:先按照IEEE 754转成相应的二进制,然后对阶运算

按照这个思路分析一下0.1 + 0.2的运算过程

1.进制转换

0.1和0.2转换成二进制后会无限循环

0.1 -> 0.0001100110011001...(无限循环)
0.2 -> 0.0011001100110011...(无限循环)
复制代码

但是由于IEEE 754尾数位数限制,需要将后面多余的位截掉(本文借助这个网站直观展示浮点数在内存中的二进制表示)

0.1

 

 

 

0.2

 

 

这样在进制之间的转换中精度已经损失

 

这里还有一个小知识点

那为什么 x=0.1 能得到 0.1?

这是因为这个 0.1 并不是真正的0.1。这不是废话吗?别急,听我解释

标准中规定尾数f的固定长度是52位,再加上省略的一位,这53位是JS精度范围。它最大可以表示2^53(9007199254740992), 长度是 16,所以可以使用 toPrecision(16) 来做精度运算,超过的精度会自动做凑整处理

0.10000000000000000555.toPrecision(16)
// 返回 0.1000000000000000,去掉末尾的零后正好为 0.1

// 但来一个更高的精度:
0.1.toPrecision(21) = 0.100000000000000005551
复制代码

这个就是为什么0.1可以等于0.1的原因。好的,继续

2.对阶运算

由于指数位数不相同,运算时需要对阶运算 这部分也可能产生精度损失

按照上面两步运算(包括两步的精度损失),最后的结果是

0.0100110011001100110011001100110011001100110011001100 
复制代码

结果转换成十进制之后就是0.30000000000000004,这样就有了前面的“秀”操作:0.1 + 0.2 != 0.3

所以:

精度损失可能出现在进制转化和对阶运算过程中

精度损失可能出现在进制转化和对阶运算过程中

精度损失可能出现在进制转化和对阶运算过程中

只要在这两步中产生了精度损失,计算结果就会出现偏差

怎么解决精度问题?

1.将数字转成整数

这是最容易想到的方法,也相对简单


function add(num1, num2) {
 const num1Digits = (num1.toString().split('.')[1] || '').length;
 const num2Digits = (num2.toString().split('.')[1] || '').length;
 const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits));
 return (num1 * baseNum + num2 * baseNum) / baseNum;
}
复制代码

但是这种方法对大数支持的依然不好

2.三方库

这个是比较全面的做法,推荐2个我平时接触到的库

1).Math.js

专门为 JavaScript 和 Node.js 提供的一个广泛的数学库。支持数字,大数字(超出安全数的数字),复数,分数,单位和矩阵。 功能强大,易于使用。

官网:mathjs.org/

GitHub:github.com/josdejong/m…

2).big.js

官网:mikemcl.github.io/big.js

GitHub:github.com/MikeMcl/big…

3)若干,不一一列举了

这几个类库都很牛逼,可以应对各种各样的需求,不过很多时候,一个函数能解决的问题不需要引用一个类库来解决。

以上就是我对js精准计算的理解,希望可以帮到大家

转载必须标明出处,谢谢。文章有疏漏浅薄之处,请各位大神斧正

说明

看了评论很多人说:其他遵循 IEEE 754 标准的语言也有这个问题,我知道其他的语言也有,但是这篇文章是以js为切入点去分析的,so不要去纠结哪种语言了,文章重点不是语言,谢谢

看了评论很多人说:其他遵循 IEEE 754 标准的语言也有这个问题,我知道其他的语言也有,但是这篇文章是以js为切入点去分析的,so不要去纠结哪种语言了,文章重点不是语言,谢谢

看了评论很多人说:其他遵循 IEEE 754 标准的语言也有这个问题,我知道其他的语言也有,但是这篇文章是以js为切入点去分析的,so不要去纠结哪种语言了,文章重点不是语言,谢谢


作者:Gladyu
链接:https://juejin.im/post/5b90e00e6fb9a05cf9080dff
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_34629352/article/details/100020922

智能推荐

【上古秘籍】之Eclipse的秘籍_eclipse tookchain空-程序员宅基地

文章浏览阅读1.1k次。CSS code. 打开MyEclipse 6.0.1,然后“window”→“Preferences”2. 选择“java”,展开,“Editor”,选择“Content Assist”。3. 选择“Content Assist”,然后看到右边,右边的“Auto-Activation”下面的“AutoActivation triggers for java”_eclipse tookchain空

(原创)分享自己写的几个工具类(四)BigDecimal精确计算_java bigdecimal常用计算工具类-程序员宅基地

文章浏览阅读2.9k次。java的float只能用来进行科学计算或工程计算,但是在大多数的商业计算中,一般采用java.math.BigDecimal类来进行精确计算。比如我们平常常见的支付宝和滴滴打车,如果要模拟运算的话就必须用到这个类BigDecimal的使用步骤如下:1初始化一个BigDecimal对象这两种方法都可以BigDecimal bd1 = new BigDecimal_java bigdecimal常用计算工具类

Android进阶解密读书笔记(十一)——理解 ClassLoader_android urlclassloader-程序员宅基地

文章浏览阅读609次。定义一个自定义 ClassLoader 并继承抽象类 ClassLoader复写 findClass 方法,并在 findClass 方法中调用 defineClass 方法。_android urlclassloader

猫眼APP抓包——爬虫_猫眼电影app 抓包-程序员宅基地

文章浏览阅读2.4k次。我们打开猫眼电影PC端官网,发现只有十几条评论,而绝大多数评论都是在App上,因此我们需要从App上获取评论数据。一般地,我们要获取App数据就必须通过抓包软件进行分析,找到对应的接口,我们找到借口后即可直接对url链接进行访问。如猫眼电影的评论接口为:http://m.maoyan.com/mmdb/comments/movie/电影代号.json?v=yes&offset=1..._猫眼电影app 抓包

Halcon例程分析7:划痕检测_surface_scratch图片-程序员宅基地

文章浏览阅读2.1k次。打开halcon,按下ctrl+e打开halcon自带例程。工业领域->光学与精密工程->surface_scratch.hdev* This programm shows the extraction of surface scratches via* local thresholding and morphological post-processing* dev_up..._surface_scratch图片

recv 和WSARecv区别_假如wsarecv接收数据不全怎么办-程序员宅基地

文章浏览阅读4.7k次。recv()是最基础的。recv和WSARecv对在已连接套接字上接受接入数据来说,recv函数是最基本的方式。它的定义如下:int recv ( SOCKET s, char FAR* buf, int len, int flags);第一个参数s,是准备接收数据的那个套接字。第二个参数buf,是即将收到数据的字符缓冲,而len则是准备接_假如wsarecv接收数据不全怎么办

随便推点

HBase HA 分布式部署_hbase ha部署-程序员宅基地

文章浏览阅读345次。1、准备工作节点test-194test-206test-210Zookeeper***zkfc**JournalNode***NameNodeactivestandbyDataNode***HMastermasterbackup-masterHRegeionServer***1、安装包apache-zookeeper-3.5.9-bin.tar.gzhadoop-3.1.4.tar_hbase ha部署

2021-03-09_吕鑫2022c++-程序员宅基地

文章浏览阅读190次。官方官方文档中文版 http://api.unrealengine.com/CHN/英文版 https://docs.unrealengine.com/en-us/虚幻学院(英文教程,未来可能有汉化)https://academy.unrealengine.com/官方B站https://space.bilibili.com/138827797/#/C++【侯捷】《C++系统工程师》课程【吕鑫】【VS2015】C/C++/数据结构教程(推荐有选择的看)http://class.qq._吕鑫2022c++

java/php/net/python计算机网络课程在线学习APP【2024年毕设】-程序员宅基地

文章浏览阅读17次。为了方便更直观的理解,下图以图形形式给出关于整个系统的结构图。springboot基于Vuejs的在线校园疫情管控服务平台。springboot无人之境智能酒店服务平台的设计与实现。springboot学生成绩动态追踪系统设计与实现。springboot图片创作分享平台的设计与实现。springboot员工考勤系统的设计与实现。springboot医院体检中心信息管理系统。springboot人脸识别物流运输管理系统。springboot漫语在线论坛设计与实现。springboot实习生校内事务管理系统。

连接mysql数据库报错:SQLSTATE[HY000] [2002] No such file or directory_in connection.php line 669: sqlstate[hy000] [2002]-程序员宅基地

文章浏览阅读1.6k次。连接mysql数据库报错:SQLSTATE[HY000] [2002] No such file or directory_in connection.php line 669: sqlstate[hy000] [2002] no such file or directory

滤波反投影算法的matlab仿真_滤波反投影的基本过程-程序员宅基地

文章浏览阅读1.2k次。本文将详细介绍滤波反投影算法的原理、实现过程,以及其变种——直接滤波反投影和解析法滤波反投影,并探讨其应用领域。:投影数据在经过Radon变换后,进行滤波和反投影操作,以恢复对象的像素值。解析法滤波反投影是滤波反投影的另一种变种,通过解析法得到投影数据的反投影,从而避免了离散化和数值计算带来的误差。直接滤波反投影是滤波反投影算法的一种变体,通过在投影域内直接滤波,而不是在图像域内滤波。将滤波后的投影数据代入解析反投影表达式,得到图像的解析反投影。对投影数据进行滤波,得到滤波后的投影数据。_滤波反投影的基本过程

一个带着框架眼镜潜心研究编程的最美程序员_独眼 程序员-程序员宅基地

文章浏览阅读787次。最美程序员 在维密,有许多我们熟知的超模:刘雯,何穗,奚梦瑶...都是一等一的好。而今天我想为大家讲述的女神故事的主人公是:Lyndsey Scott。作为模特,她是第一个与CK签下独家合同在纽约时装周走秀的非裔美国人。从此Scott在模特工作上突飞猛进,先后为普拉达,Gucci,路易斯威登,Vera Wang,Fendi等知名品牌参与模特工作..._独眼 程序员

推荐文章

热门文章

相关标签