js中toFixed的bug

toFixed四舍五入的bug:
不同浏览器对四舍五入的边界值处理是不同的。
  当边界值为5的时候,比如9.445,要保留两位小数,在谷歌浏览器中会执行舍去操作,所以9.445.toFixed(2),结果为9.44。火狐浏览器亦如此。ie为9.45。
  当四舍五入后结果为0的话,会进一;比如0.005.toFixed(2),结果为0.01.
  网上有很多文章说toFixed方法其实内部算法是银行家舍入算法(JAVA是直接存在这种舍入方法的---HALF_EVEN,这里就不作解释了),但是在大量的验证之后,其实不然,toFixed方法并不是运用了银行家舍入算法,那么这里就需要重写toFixed方法了
 
重写toFixed方法
  初次实践
toFixed(fractionDigits);
Number.prototype.toFixed=function (fractionDigits) {
  return ( parseInt((this * Math.pow( 10, fractionDigits ) + 0.5).toString())/ Math.pow( 10, fractionDigits )).toString();
}
  但是,在之后做项目,同样遇到了上述的问题,通过大量验证,及断点,上述方法存在问题,浮点数的乘除法会存在精度问题。比如2.155*100 = 215.49999
因此,需要注意,js中的乘除法净度问题:
加法:
function dcmAdd(arg1,arg2){
    var r1,r2,m;
    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));
    return (accMul(arg1,m)+accMul(arg2,m))/m;
}
减法:
function dcmAdd(arg1,arg2){
    var r1,r2,m;
    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));
    return (accMul(arg1,m)-accMul(arg2,m))/m;
}
乘法:
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);
    }
}
 
那么tofixed需要解决净度问题之后在进行小数的保留:
 
Number.prototype.toFixed = function(length){
  var carry = 0; //存放进位标志
  var num, multiple; //num为原浮点数放大multiple倍后的数,multiple为10的length次方
  var str = this + ''; //将调用该方法的数字转为字符串
  var dot = str.indexOf("."); //找到小数点的位置
  if(dot!=-1) {
    if(str.slice(dot + length + 1, dot + length + 2) >= 5) carry = 1; /*找到要进行舍入的数的位置,手动判断是否大于等于5,满足条件进位标志置为1,这里原作者用的是str.substr(dot + length + 1, 1)*/
  }
  // 需要舍入的数字如果小数位小于等于length,则不处理;
  if(this.toString().length-dot-1<=length) {
    return this;
  }
  multiple = Math.pow(10, length); //设置浮点数要扩大的倍数
  num = Math.floor(this * multiple) + carry; //去掉舍入位后的所有数,然后加上我们的手动进位数
  var result = num / multiple + ''; //将进位后的整数再缩小为原浮点数
  /*
  * 处理进位后无小数
  */
  dot = result.indexOf(".");
  if(dot === -1){
    result += '.';
    dot = result.indexOf(".");
  }
  /*
  * 处理多次进位
  */
  var len = result.length - (dot+1);
  if(len < length){
    for(var i = 0; i < length - len; i++){
      result += 0;
    }
  }
  return result;
}
 
上述是对toFixed修正为四舍五入,实际项目中会对许多的金额数值进行保留两位小数,那么可能就会用到银行家舍入算法:
    四舍六入五考虑、五后非零就进一、五后为零看奇偶、五前为偶应舍去、五前为奇要进一
Number.prototype.toFixed = function (length) {
 
    if ( length <= 0) {
      return this
    }
    if( typeof length !== 'number') {
      return 'NAN'
    }
    let num = this + '', di = num.indexOf('.')
    if (di == -1) {
      if (length == 0) {
        return num;
      } else {
        return num + "." + "0".repeat(length)
      }
    } else {
      if (length == 0) {
        return num.substring(0, di)
      } else {
        let dec = num.substring(di + 1, num.length)
        if (dec.length > length) {
          let p = +dec[length], int = num.substring(0, di + 1)
          let firstDec = +dec.substring(0, length - 1) === 0?'0':dec.substring(0, length - 1)*10
          let secondDec = +dec[length - 1] + 1
          if (secondDec === 10) {
            firstDec = firstDec*1
          }
          if (p < 5) {
              return int + dec.substring(0, length)
          } else if (p > 5) {
              return int + (firstDec + secondDec)
          } else {
              let b = +dec[length - 1], a = +dec[length + 1]
              if (a > 0) {
                  return int + (firstDec + secondDec)
              } else if (b & 1 == 1) {
                  return int + (firstDec + secondDec)
              } else {
                  return int + dec.substring(0, length)
              }
          }
        } else if (dec.length < length) {
            return num + "0".repeat(length - dec.length)
        } else {
            return num
        }
      }
    }

猜你喜欢

转载自www.cnblogs.com/guancy/p/11776634.html
今日推荐