zoukankan      html  css  js  c++  java
  • js处理浮点数计算误差

    众所周知,浮点计算会产生舍入误差的问题,比如,0.1+0.2,结果应该是0.3,但是计算的结果并不是如此,而是0.30000000000000004,这是使用基于IEEE754数值的浮点计算的通病,js并非独此一家,今天我们就来看看js怎么解决这个误差的。
    以下是针对加减乘除的解决方法:
    加法:
    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;
    }
    减法:
    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; //取位数大的
    // n = Math.max(r1, r2);
    return ((arg1 * m - arg2 * m) / m).toFixed(n);
    }
    乘法:
    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);
    }
    除法:
    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);
    }
    }
    用以上4个方法进行计算的话,就能够规避浮点数计算误差问题了,他们的原理大多是,通过去掉小数点,将浮点数变为整数,再进行计算。比如加法:
    var r1, r2, m, c; //r1为arg1的小数位数,r2为arg2的小数位数,m为arg1和arg2较大数的幂,c是位数差
    try {
    r1 = arg1.toString().split(".")[1].length; //计算arg1的位数
    }
    catch (e) {
    r1 = 0;
    }
    try {
    r2 = arg2.toString().split(".")[1].length; //计算arg2的位数
    }
    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; //将扩大了的数相加,再除以较大数的幂,将结果还原
    }
    从上面可以看出,为了的到两个小数扩大后的整数,使用的是现将他们转换成字符串,再用空去替换点号,这个方法得到的数字就是扩大后的数字,而网上流行另一种写法,就是先乘以一个倍数,将其变为整数,再相加,再除以倍数的方法,这个方法乍一看,是行的通的,但是经过测试,此方法也会存在bug。
    function add(a, b) {
    var c, d, e, h;
    try {
    c = a.toString().split(".")[1].length;
    } catch (f) {
    c = 0;
    }
    try {
    d = b.toString().split(".")[1].length;
    } catch (f) {
    d = 0;
    }
    e = Math.pow(10, Math.max(c, d));
    return (a * e + b * e) / e;
    //h = Math.max(c, d);
    //return ((mul(a, e) + mul(b, e)) / e).toFixed(h);
    }
    此方法e指的是两个数中较大数的幂,错就错在,ae,和be在计算的时候会出现计算误差,由于wiki不能上传图片,就不展示了,综上所述,我推荐第一种的解决方案。当然第二种也是有不就措施的,就是将我上面代码的两行注释放开,我们运用了toFixed将误差给截取,从而得到正确的结果。
    这里,我在提供一个测试的方法:
    function test() {
    var a = (Math.random() * 100).toFixed(2) - 0;
    var b = (Math.random() * 1000).toFixed(2) - 0;
    var result = add(a, b);
    if ((result + '').length > 10) {
    console.error('被加数:' + a, '加数:' + b, '结果:' + result);
    return;
    }
    setTimeout(function () {
    test();
    }, 10);
    }

  • 相关阅读:
    jQuery函数继承 $.extend, $.fn.extend
    [原创]茗洋AaronYang的 jquery.myselect.js 我的一次前端突破[上]
    EasyUI 的 combotree 加载数据后折叠起来,并且只允许单击子节点的写法
    判断js数组包是否包含某个元素
    JS中 HTMLEncode和HTMLDecode
    Easyui datagrid 特殊处理,记录笔记
    easyui tab上面添加右键菜单
    第三方文本框 在div中显示预览,让指定节点不受外部css影响
    Easyui 让Window弹出居中
    C# txt格式记录时间,时间对比,决定是否更新代码记录Demo
  • 原文地址:https://www.cnblogs.com/yky-iris/p/10083097.html
Copyright © 2011-2022 走看看