JavaScript 浮点数及运算精度调整总结_前端精度计算公式-程序员宅基地

技术标签: 标准  学习笔记  javascript  

JavaScript 只有一种数字类型 Number,而且在Javascript中所有的数字都是以IEEE-754标准格式表示的。浮点数的精度问题不是JavaScript特有的,因为有些小数以二进制表示位数是无穷的。

十进制            二进制 
0.1              0.0001 1001 1001 1001 … 
0.2              0.0011 0011 0011 0011 … 
0.3              0.0100 1100 1100 1100 … 
0.4              0.0110 0110 0110 0110 … 
0.5              0.1 
0.6              0.1001 1001 1001 1001 … 

所以比如 1.1,其程序实际上无法真正的表示 ‘1.1′,而只能做到一定程度上的准确,这是无法避免的精度丢失:1.09999999999999999

在JavaScript中问题还要复杂些,这里只给一些在Chrome中测试数据:

console.log(1.0-0.9 == 0.1)    //false 
console.log(1.0-0.8 == 0.2)    //false 
console.log(1.0-0.7 == 0.3)    //false 
console.log(1.0-0.6 == 0.4)    //true 
console.log(1.0-0.5 == 0.5)    //true 
console.log(1.0-0.4 == 0.6)    //true 
console.log(1.0-0.3 == 0.7)    //true 
console.log(1.0-0.2 == 0.8)    //true 
console.log(1.0-0.1 == 0.9)    //true 

那如何来避免这类 1.0-0.9 != 0.1 的非bug型问题发生呢?下面给出一种目前用的比较多的解决方案, 在判断浮点运算结果前对计算结果进行精度缩小,因为在精度缩小的过程总会自动四舍五入:

(1.0-0.9).toFixed(digits)  // toFixed() 精度参数digits须在0与20之间 
console.log(parseFloat((1.0-0.9).toFixed(10)) === 0.1)   //true 
console.log(parseFloat((1.0-0.8).toFixed(10)) === 0.2)    //true 
console.log(parseFloat((1.0-0.7).toFixed(10)) === 0.3)    //true 
console.log(parseFloat((11.0-11.8).toFixed(10)) === -0.8)   //true 

写成一个方法:

//通过isEqual工具方法判断数值是否相等 
function isEqual(number1, number2, digits){
     
  digits = digits == undefined? 10: digits; // 默认精度为10 
  return number1.toFixed(digits) === number2.toFixed(digits); 
} 
console.log(isEqual(1.0-0.7, 0.3));  //true 
//原型扩展方式,更喜欢面向对象的风格 
Number.prototype.isEqual = function(number, digits){
     
  digits = digits == undefined? 10: digits; // 默认精度为10 
  return this.toFixed(digits) === number.toFixed(digits); 
} 
console.log((1.0-0.7).isEqual(0.3)); //true 

接下来,再来试试浮点数的运算,

console.log(1.79+0.12)  //1.9100000000000001 
console.log(2.01-0.12)   //1.8899999999999997 
console.log(1.01*1.3)    //1.3130000000000002 
console.log(0.69/10)     //0.06899999999999999 

解决方案:

//加法函数,用来得到精确的加法结果 
//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。 
//调用:accAdd(arg1,arg2) 
//返回值:arg1加上arg2的精确结果 
function accAdd(arg1,arg2){ 
  var r1,r2,m; 
  try{
   r1=arg1.toString().split(".")[1].length}catch(e){
   r1=0} 
  try{
   r2=arg2.toString().split(".")[1].length}catch(e){
   r2=0} 
  m=Math.pow(10,Math.max(r1,r2)) 
  return (arg1*m+arg2*m)/m 
} 
//给Number类型增加一个add方法,调用起来更加方便。 
Number.prototype.add = function (arg){
     
  return accAdd(arg,this); 
} 
//减法函数,用来得到精确的减法结果 
//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的减法结果。 
//调用:accSub(arg1,arg2) 
//返回值:arg1减去arg2的精确结果 
function accSub(arg1,arg2){ 
  var r1,r2,m,n; 
  try{
   r1=arg1.toString().split(".")[1].length}catch(e){
   r1=0} 
  try{
   r2=arg2.toString().split(".")[1].length}catch(e){
   r2=0} 
  m=Math.pow(10,Math.max(r1,r2)); 
  //last modify by deeka 
  //动态控制精度长度 
  n=(r1>=r2)?r1:r2; 
  return ((arg1*m-arg2*m)/m).toFixed(n); 
} 
//除法函数,用来得到精确的除法结果 
//说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。 
//调用:accDiv(arg1,arg2) 
//返回值:arg1除以arg2的精确结果 
function accDiv(arg1,arg2){
     
  var t1=0,t2=0,r1,r2; 
  try{t1=arg1.toString().split(".")[1].length}catch(e){} 
  try{t2=arg2.toString().split(".")[1].length}catch(e){} 
  with(Math){ 
    r1=Number(arg1.toString().replace(".","")) 
    r2=Number(arg2.toString().replace(".","")) 
    return (r1/r2)*pow(10,t2-t1); 
  } 
} 
//给Number类型增加一个div方法,调用起来更加方便。 
Number.prototype.div = function (arg){
     
  return accDiv(this, arg); 
} 

//乘法函数,用来得到精确的乘法结果 
//说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。 
//调用:accMul(arg1,arg2) 
//返回值:arg1乘以arg2的精确结果 
function accMul(arg1,arg2) { 
  var m=0,s1=arg1.toString(),s2=arg2.toString(); 
  try{m+=s1.split(".")[1].length}catch(e){} 
  try{m+=s2.split(".")[1].length}catch(e){} 
  return  Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m) 
} 
//给Number类型增加一个mul方法,调用起来更加方便。 
Number.prototype.mul = function (arg){
     
  return accMul(arg, this); 
} 
<br>//验证一下: 
console.log(accAdd(1.79, 0.12));  //1.91 
console.log(accSub(2.01, 0.12));  //1.89 
console.log(accDiv(0.69, 10));    //0.069<br>console.log(accMul(1.01, 1.3));   //1.313 

改造之后,可以愉快地进行浮点数加减乘除操作了~

引用:
http://developer.51cto.com/art/201512/499429.htm

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

智能推荐

Base64Decoder使用时找不到包_xvlyc-程序员宅基地

文章浏览阅读568次。使用Base64加解密时,eclipse的设置public static void main(String[] args){String s=“H4sIAAAAAAAAAJVTPW/VMBT9K/wD2/kgbXVlyc/xo1Ff7JA4iNfFS6dKTJ3K72FDQgiQqkclGJgK”+“A1FB3ehQ9krdUCfs5MWJURey+N5zj2/uxzEsi5Uwel0JSqKEAJp8WLYyN0VOX5xg7CJbF2pRqVoP”+“JBeY+7AQTwppcqYFjXBEcI_xvlyc

Flutter中的MaterialButton不能使用全局主题颜色的真正原因_flutter textbutton 设置全局颜色不起作用-程序员宅基地

文章浏览阅读3.1k次。Flutter中的MaterialButton不能使用全局主题颜色的真正原因最近在使用flutter开发项目时遇到的问题,在页面中想设置下按钮的宽度,首先想到的是RaisedButton,主要是有阴影,并且官方推荐的,但是不能设置宽度和高度,然后选用其父类MaterialButton,可以使用minWidth这个属性,也有阴影等效果,但是新的问题,我设置的全局主题中的button颜色,不能应用..._flutter textbutton 设置全局颜色不起作用

.Net Core中的日志组件(Logging)-程序员宅基地

文章浏览阅读783次。1、介绍  Logging组件是微软实现的日志记录组件包括控制台(Console)、调试(Debug)、事件日志(EventLog)和TraceSource,但是没有实现最常用用的文件记录日志功能(可以用其他第三方的如NLog、Log4Net。之前写过NLog使用的文章)。2、默认配置  新建.Net Core Web Api项目,添加下面代码。 [Route("api/..._.net core logging

stm32软件模拟i2c通讯读取lm75a温度_float readtemperature(void){ unsigned char temp[2]-程序员宅基地

文章浏览阅读1.1w次,点赞16次,收藏105次。stm32硬件i2c有着一些bug,此外对于i2c这种通用的串行通信协议,从源头掌握和使用显然更加靠谱一些,当然,对于arm,还是直接操作寄存器来得方便的多。1、I2C协议1.1 i2c串行总线概述采用串行总线技术可以使系统的硬件设计大大简化、系统的体积减小、可靠性提高。同时,系统的更改和扩充更为容易。常用的串行扩展总线有:== I2C (Inter IC BUS)总线==、单总线(1-..._float readtemperature(void){ unsigned char temp[2]={0}; float temp_value=0

一文带你学习,动态规划算法-程序员宅基地

文章浏览阅读1.1k次,点赞4次,收藏7次。动态规划其实就是,给定一个问题,我们把它拆成一个个子问题,直到子问题可以直接解决。然后呢,把子问题答案保存起来,以减少重复计算。再根据子问题答案反推,得出原问题解的一种方法一般这些子问题很相似,可以通过函数关系式(DP方程)递推出来。动态规划就致力于解决每个子问题一次,减少重复计算。其核心思想就是:拆分子问题,记住过往,减少重复计算一个具体的例子:A : “上面等式的值是多少”B : 计算 “8”A : 在上面等式的左边写上 “1+” 呢?A : “此时等式的值为多少”_动态规划算法

android错误:Installation error: INSTALL_FAILED_VERSION_DOWNGRADE_packageinstaller 代码安装apk install_failed_version_do-程序员宅基地

文章浏览阅读546次。Installation error: INSTALL_FAILED_VERSION_DOWNGRADE,安装过一个开发的APP之后,需要把应用程序的安装包中的包文件目录修改一下,然后就出现了这个问题了,以前也出现过没有太注意,仔细查了一下资料,按其字面意思就是手机上安装的app版本比你当前想要安装的app版本低一些,所以只要提高版本号就好了,修改项目中AndroidManifest.xml文件中_packageinstaller 代码安装apk install_failed_version_downgrade

随便推点

登录及注册模块设置与流程图-程序员宅基地

文章浏览阅读6.7k次。原文地址:http://www.cocoachina.com/design/20170320/18918.html一、登录/注册模块流程图 1、电商&O2O类产品下单注册逻辑2、类似于简书&知乎等内容型社区产品二、关于注册流程1、同一页面中完成注册较适合填写信息较少(往往不包含关于用户个人信息的设置),注册流程简洁的产品,这类产品的注册所需的..._注册、登录、产品下单的流程图

python3之http.server模块_python http.server-程序员宅基地

文章浏览阅读5.7w次,点赞30次,收藏104次。有时候我们需要快速地搭建一个web服务,这时我们就可以使用python里面的http.server模块搭建http服务器。实现的方式有以下几种。BaseHTTPServer模块在Python3中已被合并到http.server1. 命令行启动pyhton -m http.server 80 #pyhton3中启动方式,开启的端口为80python -m SimpleHTTPServer 8080 #python2启动方式这种是常用的方式,很简单。2. 代_python http.server

「面试必背」TCP,UDP,Socket,Http网络编程面试题(快收藏)_socket面试题-程序员宅基地

文章浏览阅读3.7k次。网络通讯在系统交互中是必不可少的一部分,无论是职场面试还是工作中都是绕不过去的,本节我们来谈谈 Java 网络编程中的一些知识和网络编程面试题。1.先看一天面试的经验:2.什么是网络编程3.网络编程中两个主要的问题4.网络协议是什么在计算机网络要做到井井有条的交换数据,就必须遵守一些事先约定好的规则,比如交换数据的格式、是否需要发送一个应答信息。这些规则被称为网络协议。5.为什么要对网络协议分层简化问题难度和复杂度。由于各层之间独立,我们可以分割大问题为小_socket面试题

android游戏开发入门题大学题,android基础教程ppt-程序员宅基地

文章浏览阅读810次,点赞9次,收藏17次。对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。!文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!**

前端「HTML+CSS」零基础入门学习笔记_前端【html+css零基础入门学习笔记-程序员宅基地

文章浏览阅读1.3k次,点赞29次,收藏41次。HTML+CSS入门学习,这篇就够了!_前端【html+css零基础入门学习笔记

递推法_递推法的一般步骤-程序员宅基地

文章浏览阅读1.9k次。目录设计思想典型例题一、兔子繁殖问题二、最大公约数三、猴子吃桃问题设计思想 利用所求解问题的本身具有的性质(递推关系)来求得问题解得有效方法。具体的做法是:对于一个问题,可以根据N=n之前的一步(n-1)或多步(n-1,n-2,n-3,·····)的结果推导出n时的解:f(n)=F(f(n-1),(n-2),·····) 称为递推关系式。递推算法的关键问题是得到相邻的数据项之间的关系,即递推关系。一般步骤:(1)确_递推法的一般步骤

推荐文章

热门文章

相关标签