js实现小数点四舍五入

js实现小数点四舍五入

其实这个问题,在之前的面试中被提问到了,由于笔者平时都是用原生的toFixed()的方法来保留小数点,所以当时并没有回答出来这个问题,呜呜呜. 😹
现在突然想起了这个问题,就研究一下吧。

最简单的实现方法

可以使用Math对象的一些方法来实现,这个比较简单,主要用到了Math.round和一些简单的乘除法运算。例子
思路:
1. 先把数值转成只有一位小数点的数值
2. 利用Math.round方法四舍五入(关键)
3. 最后通过乘除法运算等到想要的小数点位数


function toFixed(num,decimal){
    if(isNaN(num)){
        return 0;
    }
    num = num-0;
    var p1 = Math.pow(10, decimal + 1);
    var p2 = Math.pow(10, decimal);
    console.log(num * p1 / 10);
    console.log(Math.round(num * p1 / 10));
    return (Math.round(num * p1 / 10) / p2).toFixed(decimal); //思考一下,为什么要除10?
}

运行一下上面的代码,其实有隐藏的bug... 💔


toFixed(2.555,2) //2.56

toFixed(4100.065,2) //4100.06 ???

console.log(4100065/10) //410006.49999999994  这就是bug的答案

0.1+0.2=? //0.30000000000000004

所谓的隐藏bug,就是js编程语言的小数点精度问题,所以上面那个除于10 只是在一定的范围内有效,过了这个范围,还是会出现精度问题...

用字符串处理

既然小数点进行运算会出现问题,那我们换一种思路,用字符串来处理。例子 😄
思路:
1. 把数字转成字符串,然后把小数点移动到倒数第二位。(模拟只有一位小数点)
2. 还是用到Math.round来四舍五入
3. 重复第一个步骤,不过把小数点移动到(你要保留多少位小数点)


function toFixed(num,decimal){
    if(isNaN(num)){
        return 0;
    }
    var strnum = num+'';
    var arr = strnum.split('.');
    if(arr.length<2){
        return num.toFixed(decimal);
    }

    strnum = arr.join('');

    var strnum2 = strnum.slice(0,-1)+'.'+strnum.slice(-1);

    var result = Math.round(strnum2-0)+'';

    if(arr[1].length==decimal){
        result+='0'; //如果小数点的个数刚好等于要保留的小数点个数,要补0
    }

    return result.slice(0,-decimal)+'.'+result.slice(-decimal)
}

2020-04 更新

上面的字符串处理不够好,下面补充个新的逻辑方式:

  1. 把数字转成字符,并记录.小数点的位置;如果没有小数点,则直接在后面补0;
  2. 通过小数点的位置,计算出原数字有多少位小数(oldPointNum);把小数点去掉并且把字符串转成数组;
  3. 通过比较oldPointNum和n(要保留的位数);如果oldPointNum<n,直接补0;否则下一步
  4. 比较数组倒数(i =oldPointNum-n)的数字是否>=5;是则进一位;i++;循环该步骤(核心),用这一步来模拟四舍五入。

const toFixed = (number, n) => {
  let numberStr = number + "";
  let reg = /^(-|\+)?(\d+(\.\d*)?|\.\d+)$/i;
  if(!reg.test(numberStr)) {
      console.error('输入的数字格式不对');
      return;
  }
  let sign = numberStr.charAt(0) === '-' ? (numberStr=numberStr.slice(1),-1):1; // 判断是否是负数
  let pointIndex = numberStr.indexOf("."); // 记录小数点的位置
  if (pointIndex > -1) {
    numberStr = numberStr.replace(".", "");
  } else { // 没有小数点直接添加补0;
    numberStr += ".";
    numberStr+=new Array(n).join('0');
    return numberStr;
  }
  let numberArray = numberStr.split(""); //转成数组
  let len = numberArray.length;
  let oldPointNum = len - pointIndex; // 获取原数据有多少位小数
  if (oldPointNum < n) { // 要保留的小数点比原来的要大,直接补0
    while (n - oldPointNum > 0) {
      numberArray.push(0);
      n--;
    }
  } else if (oldPointNum > n) { // 模拟四舍五入
    
    let i = oldPointNum - n; // 从倒数第i个数字开始比较
    let more = numberArray[len - i] >= 5 ? true : false;
    while (more) {
      i++;
      more = +numberArray[len - i] + 1 === 10 ? true : false; // 进位后判断是否等于10,是则继续进位
      numberArray[len - i] = more&&i!==(len+1) ? 0 : +numberArray[len - i] + 1; // 其他位置的数字进位直接变成0,第一位的例外
      console.log(i, len);
    }
    numberArray.length = len- (oldPointNum-n); // 截取多余的小数
  }
  numberArray.splice(pointIndex, 0, ".");
  return sign===-1?'-'+numberArray.join(""):numberArray.join("");
};

ps :上面的代码有点臃肿,其实可以通过正则来简化,以后有空修改。

posted @ 2019-07-01 14:07  =.=  阅读(8993)  评论(1编辑  收藏  举报