问题描述

在JS计算四则运算时会遇到精度丢失的问题,会引起诸多问题,看看以下例子:

例如:在chrome控制台输入 0.1 + 0.7

输出结果是 0.7999999999999999

例如:0.1+0.2

输出结果:0.30000000000000004

例如:0.1277*1000000000000

输出结果:127700000000.00002

问题代码

bMsg.expectRate的值为0.1277

bMsg.expectRate * 1000000000000 / 10000000000

如问题描述的输出结果可以看到,结果并不是预期的12.77,而是127700000000.00002

问题原因分析

问题原因是Javascript采用了IEEE-745浮点数表示法(几乎所有的编程语言都采用),这是一种二进制表示法,可以精确地表示分数,比如1/2,1/8,1/1024。遗憾的是,我们常用的分数(特别是在金融的计算方面)都是十进制分数1/10,1/100等。二进制浮点数表示法并不能精确的表示类似0.1这样 的简单的数字。

二进制浮点数表示

0.1 + 0.2计算过程会转化成二进制,但由于换算为二进制表达时是无穷的。例如:

0.1 -> 0.0001100110011001...(无限)

0.2 -> 0.0011001100110011...(无限)

IEEE 754 标准的 64 位双精度浮点数的小数部分最多支持 53 位二进制位,所以两者相加之后得到二进制为

0.0100110011001100110011001100110011001100110011001100

因浮点数小数位的限制而截断的二进制数字,再转换为十进制,就成了 0.30000000000000004。所以在进行算术计算时会产生误差。

解决方案

为了解决浮点数运算精度丢失问题,一般有以下的方案:

使用类库

toFixed方法

在IE6、7、8版本会存在兼容性问题,返回值不准确。

小数部分转化为整数,计算后再转化成小数(推荐使用)

以下是一些封装好的方法

加法函数

/**
** 加法函数,用来得到精确的加法结果
** 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
** 调用:accAdd(arg1,arg2)
** 返回值:arg1加上arg2的精确结果
**/
function accAdd(arg1, arg2) {
var r1, r2, m, c;
try {
r1 = arg1.toString().split(".")[1].length;
}
catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
}
catch (e) {
r2 = 0;
}
c = Math.abs(r1 - r2);
m = Math.pow(10, Math.max(r1, r2));
if (c > 0) {
var cm = Math.pow(10, c);
if (r1 > r2) {
arg1 = Number(arg1.toString().replace(".", ""));
arg2 = Number(arg2.toString().replace(".", "")) * cm;
} else {
arg1 = Number(arg1.toString().replace(".", "")) * cm;
arg2 = Number(arg2.toString().replace(".", ""));
}
} else {
arg1 = Number(arg1.toString().replace(".", ""));
arg2 = Number(arg2.toString().replace(".", ""));
}
return (arg1 + arg2) / 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);
} // 给Number类型增加一个mul方法,调用起来更加方便。
Number.prototype.sub = function (arg) {
return accMul(arg, this);
};

乘法函数

/**
** 乘法函数,用来得到精确的乘法结果
** 说明: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);
};

除法函数

/**
** 除法函数,用来得到精确的除法结果
** 说明: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);
};

更多参考

JS浮点计算精度问题分析与解决的更多相关文章

  1. JS 浮点计算BUG

    最近做项目的时候遇到一个比较纠结的js浮点计算问题. 当时是做利率计算,因为利率大多数涉及到小数点,精度要求也很高. 0.6+0.1+0.1=? 结果出现:0.7999999999999 网上查找了一 ...

  2. JavaScript中的ParseInt("08")和“09”返回0的原因分析及解决办法

    今天在程序中出现一个bugger ,调试了好久,最后才发现,原来是这个问题. 做了一个实验: alert(parseInt("01")),当这个里面的值为01====>07时 ...

  3. js中精度问题以及解决方案

    js中的数字按照IEEE 754的标准,使用64位双精度浮点型来表示.其中符号位S,指数位E,尾数位M分别占了1,11,52位,并且在ES5规范中指出了指数位E的取值范围是[-1074, 971]. ...

  4. Eclipse编辑jsp、js文件时卡死现象的解决办法汇总

    使用Eclipse编辑jsp.js文件时,经常出现卡死现象,在网上百度了N次,经过N次优化调整后,卡死现象逐步好转,具体那个方法起到作用,不太好讲.将所有用过的方法罗列如下: 1.取消验证 windo ...

  5. Long类型转json时前端js丢失精度解决方案

    一.问题背景 Java后端开发过程中,尤其是id字段,因数值太大,通过json形式传输到前端后,在js解析时,会丢失精度. 如果对精度丢失没有什么概念,可以看一个知乎的帖子,来感受一下:https:/ ...

  6. ExtJs 通过分析源代码解决动态加载Controller的问题

    通过分析源代码解决动态加载Controller的问题 最近在研究ExtJs(4.2.0)的MVC开发模式,具体Extjs的MVC如何使用这里不解释,具体参见ExtJs的官方文档.这里要解决的问题是如何 ...

  7. nginx和Tomcat集成后发生的重定向问题分析和解决

    nginx和Tomcat集成后发生的重定向问题分析和解决 Tomcat前端配置一个HTTP服务器应该是大部分应用的标配了,基本思路就是所有动态请求都反向代理给后端的Tomcat,HTTP服务器来处 理 ...

  8. Mybatis关联查询和数据库不一致问题分析与解决

    Mybatis关联查询和数据库不一致问题分析与解决 本文的前提是,确定sql语句没有问题,确定在数据库中使用sql和项目中结果不一致. 在使用SpringMVC+Mybatis做多表关联时候,发现也不 ...

  9. C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析与解决方法

    对于C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析,目前本人分析两种情况,如下: 情况一: 借鉴麒麟.NET ...

随机推荐

  1. 题解-CTSC2012 熟悉的文章

    Problem bzoj 题目大意:给定多个标准串和一个文本串,全部为01串,如果一个串长度不少于\(L\)且是任意一个标准串的子串,那么它是"熟悉"的.对于文本串\(A\),把\ ...

  2. 修改docker image存放位置

    修改镜像和容器的默认存放路径 指定镜像和容器存放路径的参数是--graph=/var/lib/docker,我们只需要修改配置文件指定启动参数即可.刚好有个300g盘的挂在/data目录上,所以在这个 ...

  3. $Django 路飞之小知识回顾,Vue之样式element-ui,Vue绑定图片--mounted页面挂载--路由携带参数

    一 小知识回顾 1 级联删除问题 2 一张表关联多个表,比如有manytomanyfileds forignkey,基于对象查询存在的问题:反向查询的时候  表名小写_set.all()不知是哪个字段 ...

  4. python学习第38天

    mysql的存储引擎(innodb,myisam)mysql支持的数据类型约束表的创建\删除\修改\查看表结构表与表之间的关系

  5. python创建udp服务端和客户端

    1.udp服务端server from socket import * from time import ctime HOST = '' PORT = 8888 BUFSIZ = 1024 ADDR ...

  6. 疯狂Workflow讲义——基于Activiti的工作流应用开 PDF 下载

    <疯狂Workflow讲义--基于Activiti的工作流应用开> 一:文档获取下载方式: 1:花20CSDN积分:可以下载:http://download.csdn.net/downlo ...

  7. Confluence 6 针对你的数据库类型确定校验 SQL

    不同的数据库通常要求不同的 SQL 校验查询.校验查询通常需要尽可能的简单,这个查询在链接从数据库连接池中取出的时候都会被执行一次. 针对不同的数据库类型,我们推荐先的校验查询 SQL: MySQL ...

  8. 对一个元素 同时添加单击onclick 和 双击ondblclick 触发冲突的解决

    需求说明:单击列表项内容后,吧啦吧啦,双击列表项内容后,巴拉巴拉巴拉~~~ 解决思路:卧槽 ,其实我是没思路的,当时唯一的想法就是,看个人点击鼠标的速度了,双击快一点,触发双击事件ლ(′◉❥◉`ლ), ...

  9. bzoj 1495

    这是一道...卡了我一个月的树形dp... 我真是太弱了... 其实仔细想想,这题的核心思路并不是特别复杂,但是的确存在不小的难度 作为一个看过全网基本所有题解+标程才弄明白这题到底怎么回事的蒟蒻,我 ...

  10. Python中的xxx == xx是否等价于xxx is xxx

    先上代码: >>> a = 1 >>> b = 1 >>> a is b True >>> a == b True what? ...