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 相关文章推荐
实测jquery data()如何存值
Aug 18 Javascript
javaScript如何处理从java后台返回的list
Apr 24 Javascript
JS组件Bootstrap Select2使用方法解析
May 30 Javascript
利用jquery实现瀑布流3种案例
Sep 18 Javascript
Vue.directive自定义指令的使用详解
Mar 10 Javascript
js利用for in循环获取 一个对象的所有属性以及值的实例
Mar 30 Javascript
js推箱子小游戏步骤代码解析
Jan 10 Javascript
基于JS实现带动画效果的流程进度条
Jun 01 Javascript
使用electron制作满屏心特效的示例代码
Nov 27 Javascript
webpack+vue-cil 中proxyTable配置接口地址代理操作
Jul 18 Javascript
理解JavaScript中的对象
Aug 25 Javascript
vue中利用mqtt服务端实现即时通讯的步骤记录
Jul 01 Vue.js
利用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
一段防盗连的PHP代码
2006/12/06 PHP
php include和require的区别深入解析
2013/06/17 PHP
遍历echsop的region表形成缓存的程序实例代码
2016/11/01 PHP
PHP实现的简单AES加密解密算法实例
2017/05/29 PHP
PHP应用跨时区功能的实现方法
2019/03/21 PHP
Thinkphp5框架简单实现钩子(Hook)行为的方法示例
2019/09/03 PHP
用js判断浏览器是否是IE的比较好的办法
2007/05/08 Javascript
JavaScript之appendChild、insertBefore和insertAfter使用说明
2010/12/30 Javascript
如何将网页表格内容导入excel
2014/02/18 Javascript
JavaScript立即执行函数的三种不同写法
2014/09/05 Javascript
JS中字符串trim()使用示例
2015/05/26 Javascript
在JavaScript中处理数组之reverse()方法的使用
2015/06/09 Javascript
javascript实现简单的进度条
2015/07/02 Javascript
jQuery简单实现彩色云标签效果示例
2016/08/01 Javascript
jQuery web 组件 后台日历价格、库存设置的代码
2016/10/14 Javascript
JavaScript之filter_动力节点Java学院整理
2017/06/28 Javascript
BootStrap自定义popover,点击区域隐藏功能的实现
2018/01/23 Javascript
vue+express 构建后台管理系统的示例代码
2018/07/19 Javascript
node.js遍历目录的方法示例
2018/08/01 Javascript
对vue事件的延迟执行实例讲解
2018/08/28 Javascript
Python基于回溯法解决01背包问题实例
2017/12/06 Python
Python中文件的读取和写入操作
2018/04/27 Python
tensorflow实现简单的卷积网络
2018/05/24 Python
python网络编程之多线程同时接受和发送
2019/09/03 Python
解决python虚拟环境切换无效的问题
2020/04/30 Python
浅谈matplotlib 绘制梯度下降求解过程
2020/07/12 Python
实例教程 一款纯css3实现的数字统计游戏
2014/11/10 HTML / CSS
HTML5 Plus 实现手机APP拍照或相册选择图片上传功能
2016/07/13 HTML / CSS
基于HTML5+CSS3实现简单的时钟效果
2017/09/11 HTML / CSS
贝尔帐篷精品店:Bell Tent Boutique
2019/06/12 全球购物
介绍下Java中==和equals的区别
2013/09/01 面试题
求职者简历中的自我评价
2013/10/20 职场文书
置业顾问岗位职责
2014/03/02 职场文书
党员违纪检讨书怎么写
2014/11/01 职场文书
银行求职信模板
2015/03/20 职场文书
PHP基本语法
2021/03/31 PHP