Js运算引发的精度丢失问题

遇到的问题

  最近被公司分配到医保接口研发,天天与数值打交道,例如单价、数量、总价等...
  因为我们系统与医保接口对接需要对各个数值比对,有一家的接口要求毫厘不差,那么问题来了,我们发现两边计算结果不一致!我们在前台对计算结果做了.tofiexd(2)处理,传出一个保留两位小数的计算结果,但是在某些特定的值会出现0.01的误差,查了好几遍代码,发现逻辑并没有问题,后来发现是Js在做浮点数运算时候返回的结果不准确。

问题分析

  我们取到一个比较特殊的数值,获得的结果应该是7.53,然而我们.fixed(2)获取到的值却是7.52 。当我们去alter这个数的原值时,我们应该得到的是7.525,四舍五入得到正确值,然而现实却是得到7.52499999999...9,看到这里应该也发现了,不是.fixed()的问题,而是JS计算有问题,得到的值精度已经丢失了。这主要是由于三进制和二进制转换引起的,具体原因分析请看js小数运算不准问题的分析,如果只是想解决问题可略过。

尝试解决

  已经发现问题了,那肯定是着手解决了,毕竟关系到钱,不管在什么系统里都是大问题。同事提出先取四位小数,再分别进行 num.fixed(3);num.fixed(2); 这样处理确实能过滤掉一些特殊情况,但是还是马上被否决了。大家可以拿一种特殊数字来试试X.XX49 发现了吗?如果我们按照正常的四舍五入应该得到的是X.XX,而通过这种方式获得的确是X.X(X+1)。在处理金额的敏感方法中,这样做显然不行!后来我们又尝试了**.toPrecision()方法,期望通过指数处理这种方式获得正确的小数位,显然,我们又失败了。 ###解决之道  Js使用如此广泛,我们坚信肯定有大佬处理过这个问题。在最后我们在网上找到了解决之道**:重写Js的运算,贴出代码

/** 
* 加法运算,避免数据相加小数点后产生多位数和计算精度损失。 
* 
* @param num1加数1 | num2加数2 
*/ 
function numAdd(num1, num2) { 
var baseNum, baseNum1, baseNum2; 
try { 
baseNum1 = num1.toString().split(".")[1].length; 
} catch (e) { 
baseNum1 = 0; 
} 
try { 
baseNum2 = num2.toString().split(".")[1].length; 
} catch (e) { 
baseNum2 = 0; 
} 
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2)); 
return (num1 * baseNum + num2 * baseNum) / baseNum; 
}; 
/** 
* 加法运算,避免数据相减小数点后产生多位数和计算精度损失。 
* 
* @param num1被减数 | num2减数 
*/ 
function numSub(num1, num2) { 
var baseNum, baseNum1, baseNum2; 
var precision;// 精度 
try { 
baseNum1 = num1.toString().split(".")[1].length; 
} catch (e) { 
baseNum1 = 0; 
} 
try { 
baseNum2 = num2.toString().split(".")[1].length; 
} catch (e) { 
baseNum2 = 0; 
} 
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2)); 
precision = (baseNum1 >= baseNum2) ? baseNum1 : baseNum2; 
return ((num1 * baseNum - num2 * baseNum) / baseNum).toFixed(precision); 
}; 
/** 
* 乘法运算,避免数据相乘小数点后产生多位数和计算精度损失。 
* 
* @param num1被乘数 | num2乘数 
*/ 
function numMulti(num1, num2) { 
var baseNum = 0; 
try { 
baseNum += num1.toString().split(".")[1].length; 
} catch (e) { 
} 
try { 
baseNum += num2.toString().split(".")[1].length; 
} catch (e) { 
} 
return Number(num1.toString().replace(".", "")) 
* Number(num2.toString().replace(".", "")) / Math.pow(10, baseNum); 
}; 
/** 
* 除法运算,避免数据相除小数点后产生多位数和计算精度损失。 
* 
* @param num1被除数 | num2除数 
*/ 
function numDiv(num1, num2) { 
var baseNum1 = 0, baseNum2 = 0; 
var baseNum3, baseNum4; 
try { 
baseNum1 = num1.toString().split(".")[1].length; 
} catch (e) { 
baseNum1 = 0; 
} 
try { 
baseNum2 = num2.toString().split(".")[1].length; 
} catch (e) { 
baseNum2 = 0; 
} 
with (Math) { 
baseNum3 = Number(num1.toString().replace(".", "")); 
baseNum4 = Number(num2.toString().replace(".", "")); 
return (baseNum3 / baseNum4) * pow(10, baseNum2 - baseNum1); 
} 
}; 

重写了加减乘除后终于解决了精度问题,后来出现从数据库中得到单价取值不准确,由于赶进度直接用重写的方法单价*1处理了,也没继续深究原因。

转载于:https://my.oschina.net/dasven/blog/906188

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值