JavaScript浮点数及运算精度调整详解


Posted in Javascript onOctober 21, 2016

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

改造之后,可以愉快地进行浮点数加减乘除操作了~以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Eval and new funciton not the same thing
Dec 27 Javascript
JavaScript中this的使用详解
Nov 08 Javascript
js封装可使用的构造函数继承用法分析
Jan 28 Javascript
jQuery实现的左右移动焦点图效果
Jan 14 Javascript
JavaScript实现跑马灯抽奖活动实例代码解析与优化(二)
Feb 16 Javascript
AngularJS $injector 依赖注入详解
Sep 14 Javascript
使用jquery给新生的th绑定hover事件的实例
Feb 10 Javascript
jsTree事件和交互以及插件plugins详解
Aug 29 Javascript
JavaScript数组方法的错误使用例子
Sep 13 Javascript
Vue常用的几个指令附完整案例
Nov 06 Javascript
微信小程序用户盒子、宫格列表的实现
Jul 01 Javascript
javascript实现一款好看的秒表计时器
Sep 05 Javascript
利用Node.JS实现邮件发送功能
Oct 21 #Javascript
bootstrap中使用google prettify让代码高亮的方法
Oct 21 #Javascript
BootStrap网页中代码显示用法详解
Oct 21 #Javascript
网页瀑布流布局jQuery实现代码
Oct 21 #Javascript
js运动事件函数详解
Oct 21 #Javascript
javascript轮播图算法
Oct 21 #Javascript
使用BootStrap和Metroui设计的metro风格微网站或手机app界面
Oct 21 #Javascript
You might like
smarty静态实验表明,网络上是错的~呵呵
2006/11/25 PHP
PHP与MySQL开发中页面乱码的产生与解决
2008/03/27 PHP
php利用iframe实现无刷新文件上传功能的代码
2011/09/29 PHP
Yii框架中 find findAll 查找出制定的字段的方法对比
2014/09/10 PHP
php管理nginx虚拟主机shell脚本实例
2014/11/19 PHP
PHP获取ip对应地区和使用网络类型的方法
2015/03/11 PHP
PHP编写的图片验证码类文件分享
2016/06/06 PHP
php制作圆形用户头像的实例_自定义封装类源代码
2017/09/18 PHP
Array栈方法和队列方法的特点说明
2014/01/24 Javascript
jquery使用$(element).is()来判断获取的tagName
2014/08/24 Javascript
JS实现用户注册时获取短信验证码和倒计时功能
2016/10/27 Javascript
Dropzone.js实现文件拖拽上传功能(附源码下载)
2016/11/22 Javascript
文本溢出插件jquery.dotdotdot.js使用方法详解
2017/06/22 jQuery
微信小程序 自定义消息提示框
2017/08/06 Javascript
关于Ajax的原理以及代码封装详解
2017/09/08 Javascript
ES6学习笔记之map、set与数组、对象的对比
2018/03/01 Javascript
浅谈Vue响应式(数组变异方法)
2018/05/07 Javascript
解决vue无法设置滚动位置的问题
2018/10/07 Javascript
JS使用canvas中的measureText方法测量字体宽度示例
2019/02/02 Javascript
[09:34]2018DOTA2国际邀请赛寻真——永不放弃的iG
2018/08/14 DOTA
低版本中Python除法运算小技巧
2015/04/05 Python
Python利用Beautiful Soup模块搜索内容详解
2017/03/29 Python
利用Python破解斗地主残局详解
2017/06/30 Python
代码详解django中数据库设置
2019/01/28 Python
Appium+python自动化怎么查看程序所占端口号和IP
2019/06/14 Python
pandas.read_csv参数详解(小结)
2019/06/21 Python
Python使用lambda抛出异常实现方法解析
2020/08/20 Python
利用CSS3把图片变成灰色模式的实例代码
2016/09/06 HTML / CSS
英国领先的在线药房:Pharmacy First
2017/09/10 全球购物
Jacadi Paris英国官网:法国童装品牌
2019/08/09 全球购物
Python如何实现单例模式
2016/06/03 面试题
毕业生幼师求职自荐信
2013/10/01 职场文书
火车来了教学反思
2014/02/11 职场文书
员工三分钟演讲稿
2014/08/19 职场文书
学校运动会报道稿
2014/09/23 职场文书
在 SQL 语句中处理 NULL 值的方法
2021/06/07 SQL Server