JS浮点计算精度问题分析与解决

问题描述

在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);
};

更多参考

时间: 2024-10-12 20:37:39

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

【javascript】浮点数运算问题分析及解决方法

问题: 在用 js 进行小数四则运算时发现了一个重大问题,比如:0.7 * 0.8 = 0.5599999999999999 分析: 在 js 中只有一种数字类型 Number,而且在 js 中所有的数字都是以 IEEE-754 标准格式表示的.浮点数的精度问题并不是 js 特有的,因为有些小数以二进制表示位数是无穷的,比如 1.1,其程序实际上无法真正的表示 1.1,而只能做到一定程度上的准确(1.09999999999999999),这是无法避免的精度丢失. 通过 chrome 控制台,我们

JS 浮点计算BUG

最近做项目的时候遇到一个比较纠结的js浮点计算问题. 当时是做利率计算,因为利率大多数涉及到小数点,精度要求也很高. 0.6+0.1+0.1=? 结果出现:0.7999999999999 网上查找了一下,这确实是一个缺陷(Bug) 不仅加,只要涉及到浮点计算减成除一样会出现类似情况 先看看Demo: 将0.1~10,加0.1+0.1  进行测试 1 $(function () { 2 var content = ""; 3 for (var i = 0.1; i <= 10; i

JavaScript中的ParseInt(&quot;08&quot;)和“09”返回0的原因分析及解决办法

今天在程序中出现一个bugger ,调试了好久,最后才发现,原来是这个问题. 做了一个实验: alert(parseInt("01")),当这个里面的值为01====>07时都是正常的,但是在"08","09"就会返回0 (这种现象出现在ie内核的浏览器中,如360浏览器就会出现这种错误)(谷歌,火狐不受影响). 查阅资料得知着这种现象原因: 大神的解释: 01--07自然没有问题,但是09,08都是不合格的八进制形式,所以被按照0处理了.

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

nginx和Tomcat集成后发生的重定向问题分析和解决 Tomcat前端配置一个HTTP服务器应该是大部分应用的标配了,基本思路就是所有动态请求都反向代理给后端的Tomcat,HTTP服务器来处 理静态请求,包括图片.js.css.html以及xml等.这样可以让你的应用的负载能力提高很多,前端这个HTTP服务器主流用的最多的当属 Apache HTTP Server和nginx.今天这篇文章主要讲解的是这种组合的方式的前提下,后端的Tomcat中的app在301跳转的时候遇到的一个问题. 问

长整型Long返回到前端,js出现精度丢失怎么办

前端后接口对接时,如果后台的id或者其他字段使用了长整型Long,就很容易出现js丢失精度问题.用接口工具调用时,往往id返回都是对的,但是一到页面上,js就后缀000.前端说返回的值有问题,F12看看!后端说没问题,Postman看看!这是js支持的问题,解决这个问题的办法很显然,返回字符串即可.怎么实现呢,你可以直接将vo对象的id改成String类型,但是会带来copyProperties的不便,所以见过很多人通过增加虚拟字段实现.下面是一个整体解决方案:(需要略微了解下Spring的消息

&#160; 挖矿程序minerd,wnTKYg入侵分析和解决

挖矿程序minerd,wnTKYg入侵分析和解决                                   作者:CYH 一.起因:最近登陆一台redis服务器 发现登陆的时间非常长,而且各种命令敲大显示出的内容延迟很高 二,分析:  首先我安装了iftop监控em1这个网卡流量 Iftop -i  em1 发现里面的流量使用很低,没多少服务使用大的流量,没占用带宽,排除了ddos大流量攻击可能 接着我输入命令top 查看各种负载,一看吓一跳,cpu平均负载达到70左右,使用率达到99

js中style.display=&quot;&quot;无效的解决方法

本文实例讲述了js中style.display=""无效的解决方法.分享给大家供大家参考.具体解决方法如下: 一.问题描述: 在js中我们有时想动态的控制一个div显示或隐藏或更多的操作,但如果我们style.display=""可能导致没有效果. 看下面一段代码: 复制代码代码如下: <style> #name {     display:none; }</style></head><body><div id=

SQL Server2005索引碎片分析和解决方法

SQL Server2005索引碎片分析和解决方法 本文作者(郑贤娴),请您在阅读本文时尊重作者版权. 摘要: SQL Server,为了反应数据的更新,需要维护表上的索引,因而这些索引会形成碎片.根据工作量的特征,这些碎片会影响对应的工作性能.此文帮助决定是否需要整理碎片以改善性能的信息.SQL Serve提供一些命令来实现索引的碎片整理.这里比较其中两个命令:DBCC DBREINDEX 和 DBCC INDEXDEFRAG. 关键词: SQL Server;索引碎片;数据库优化毫无疑问,给

关于c++与java中文乱码问题分析与解决

关于c++与java中文乱码问题分析与解决 DionysosLai([email protected])  2014/8/1 问题分析: 之所以会出现中文乱码问题,归根结底在于中文的编码与英文的编码方式存在差异. 在java内部是使用16bit的unicode编码(即utf-16)来表示字符串,无论英文还是中文都是2字节. C/C++使用的是原始数据,ascii是一个字节,中文一般是GB2312编码,用2个字节表示一个汉字. Jni内部是使用utf-8编码表示字符串的,utf-8是扁长的unic