您好,欢迎来到华佗小知识。
搜索
您的当前位置:首页js做四则运算时,精度丢失问题及解决方法

js做四则运算时,精度丢失问题及解决方法

来源:华佗小知识
js做四则运算时,精度丢失问题及解决⽅法

⼀、前⾔:

这个问题可以说是程序员必踩的坑,因此⽹上针对该问题的分析有很多也很详细,解决⽅法也⽐较统⼀,写法也是⼤同⼩异,本以为预期效果真能如他们所说是完美的,然⽽效果却是差强⼈意。⼆、问题:

⾸先,先来看看两数相加的⼀个经典问题,⽹上找过不少资料的⼈会发现,⼤多数⼈分析精度问题都是由此展开,然⽽最后所谓的解决⽅法成也在它,败也在它。

0.1+0.2=0.30000000000000004

⽹上所说的完美解决⽅法:

function add(arg1,arg2){

var digits1,digits2,maxDigits;

try{digits1=arg1.toString().split(\".\")[1].length}catch(e){digits1=0} try{digits2=arg2.toString().split(\".\")[1].length}catch(e){digits2=0} maxDigits=Math.pow(10,Math.max(digits1,digits2)) return (arg1*maxDigits+arg2*maxDigits)/maxDigits}

应⽤后的输出结果:

0.1+0.2=0.3

从结果可以看出,这⽅法是完美的解决了这个经典问题,那其他数做运算是不是也都能完美解决呢?在我多数测试后发现并没有,本以为有可能是开发环境或者浏览器导致的问题,但是切换了多个开发环境和浏览器也还是如此,因此可知,⽅法本⾝就有问题。两数直接相加的输出结果:

2.22+2.22=4.444.44+2.22=6.666.66+2.22=8.88

8.88+2.22=11.10000000000000111.10+2.22=13.32

13.32+2.22=15.540000000000001

应⽤⽹上找的⽅法的输出结果:

2.22+2.22=4.44

4.44+2.22=6.6600000000000016.66+2.22=8.88

8.88+2.22=11.10000000000000111.10+2.22=13.3213.32+2.22=15.54

对⽐两次结果可以发现,有些数的精度问题是解决了,⽽有些数根本没有解决,甚⾄还导致有些本不会出现精度问题的数产⽣了问题,除了加法,关于减乘除⽹上那些⽅法也⼀概⾏不通,限于篇幅,这⾥就不再⼀⼀举例。三、解决⽅法:

最后发现那些⽅法都有个共同特征,就是将原来带n位⼩数的浮点数乘以10的n次⽅再进⾏运算,但是没有解决的主要问题,还是数据类型运算。只要存在浮点数的运算,怎么算也都会有精度问题。

因此,还是要保证参与运算的数为整数才⾏。保证为整数其实这点就是他们⽅法中将原来带n位⼩数的浮点数乘以10的n次⽅,但还需要强制指定类型为int。但是如果直接将乘以10的n次⽅后的数转换为Integer对象,会导致在乘⽅时出现精度问题⽽出现精度丢失,即计算结果有误。因此还是需要利⽤Math库中round⽅法,将数四舍五⼊取整再进⾏运算。两数相加⽅法:

function add(arg1, arg2) {

  return (Math.round(arg1 * 100) + Math.round(arg2 * 100)) / 100;}

应⽤该⽅法进⾏两数相加的运算结果:

0.1+0.2=0.32.22+2.22=4.444.44+2.22=6.666.66+2.22=8.888.88+2.22=11.111.10+2.22=13.3213.32+2.22=15.54

两数相减(既⼀正数加⼀负数):

function subtract(arg1, arg2) {  return this.add(arg1, -arg2);}

两数相乘:

function multiple(arg1, arg2) {

  return (Math.round(arg1 * 100) * Math.round(arg2 * 100)) / 10000;}

两数相除:

/**

* arg1与arg2相除,并以四舍五⼊的⽅式保留⼩数点后2位*/

function divide(arg1, arg2) {var d1, d2,

n1 = Number(arg1.toString().replace(\".\", \"\")),n2 = Number(arg2.toString().replace(\".\", \"\"));

try {d1 = arg1.toString().split(\".\")[1].length;} catch (e) {d1 = 0;}try {d2 = arg2.toString().split(\".\")[1].length;} catch (e) {d2 = 0;}return this.toFixed((n1 / n2) * Math.pow(10, d2 - d1), 2);}

/**

* arg以四舍五⼊的⽅式保留⼩数点后n位*/

function toFixed(arg, n) {if(n == 0) {

return Math.round(arg)}try {

var d, carryD,

ds = arg.toString().split('.'), // 分割整数和⼩数len = ds[1].length

// arg的⼩数位数超出位数n才需要处理if (len > n) {

// 先截取前n位⼩数

d = Number(ds[1].substring(0, n))

// 舍去的⼩数部分是否能够进⼀:通过 0.[第n位⼩数后的数] 四舍五⼊取整,结果为0或1carryD = Math.round(Number('0.' + ds[1].substring(n, len)))len = (d + carryD).toString().lengthif (len > n) {

// 当d=9...,carryD=1,相加后就得向整数进位了,直接返回 整数部分+1return Number(ds[0]) + 1} else if (len == n) {

return Number(ds[0] + '.' + (d + carryD))}

// ⼩数以0开头要往前补0var b0 = '', k = 0

while(k < n && ds[1].substring(0, ++k) == '0') {b0 += '0'}

return Number(ds[0] + '.' + b0 + (d + carryD))}

} catch (e) {}return arg}

注:js中⾃带的toFixed函数会把多余的⼩数直接截掉,因此,这⾥我进⾏重写,采⽤四舍五⼊的⽅式保留⼩数,该⽅法也很实⽤。————————————————

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo0.cn 版权所有 湘ICP备2023017654号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务