上图看效果

javascript 小数处理 javascript小数计算_数位

如果小数直接做运算,很容易发生如上图的精度丢失的情况,这个问题还是百度:

javascript 小数处理 javascript小数计算_数位_02

javascript 中同理。

为尽量降低这种精度丢失对数据的准确性的影响,做了相关的处理,函数如下:

/**
	 * 计算小数位数的函数
	 * @param {String|Number} val 用于计算小数位数的数值
	 * 
	*/
	function getPointLength(val){
	  // 去除尾部得零得操作
	  val = Number(val);
	  if(String(val) == 'NaN'){
	    throw new Error("传入运算的参数必须是数值 - getPointLength")
	  }
	  let index = String(val).indexOf('.');
	  let pointLength = index !== -1 ? String(val).length - (index + 1) : 0;
	  return pointLength;
	}
	
	/**
	 * 处理小数的运算 -- 目前只接受两位数
	 * @param {Number|String} val1 参数1 
	 * @param {Number|String} val2 参数2 
	 * @param {String} arithmeticalMode 运算方式,加减乘除 + - * /, 默认是加法
	*/
	function dealDecimalArithmetic(val1, val2, arithmeticalMode = '+'){
	  // 校验传入的运算参数是否是数值
	  if(String(Number(val1)) == 'NaN' || String(Number(val1)) == 'NaN'){
	    throw new Error("传入运算的参数必须是数值")
	  }
	  if(arithmeticalMode == '/' && !val2){
	    throw new Error("被除数不能为空且不能为零")
	  }
	  let pointLength = 0, res = 0;
	  // 这是总的小数位数
	  pointLength = getPointLength(val1) + getPointLength(val2);
	  // 处理,按照整数进行计算
	  switch(arithmeticalMode){
	    case "+" :
	      res = Number(String(val1).replace(".",'')) + Number(String(val2).replace(".",''));
	      break;
	    case "-" :
	      res = Number(String(val1).replace(".",'')) - Number(String(val2).replace(".",''));
	      break;
	    case "*" :
	      res = Number(String(val1).replace(".",'')) * Number(String(val2).replace(".",''));
	      break;
	    case "/" :
	      res = Number((val1 / val2).toFixed(2));
	      break;
	  }
	  // 处理小数
	  if(arithmeticalMode !== '/'){
	    res = dealNumberPoint(res, pointLength);
	  }
	  // 长度超出范围,则进行抛错处理
	  if(String(res).length > 15){
	    throw new Error("精度丢失,无法进行准确运算")
	  }
	  return res;
	}
	
	/**
	 * 计算完成之后,根据计算结果与小数位置,补全小数
	 * @param {Integer} val 用于做小数补全处理的数值,必须是整数
	 * @pointLength {Integer} pointLength 两个参数总共的小数位数
	*/
	function dealNumberPoint(val, pointLength){
	  // 校验传入的处理值是否是小数。是就抛错 -- 小数位数长度在前面的处理中就应该全部做完
	  if(Number(val) % 1 !== 0){
	    throw new Error("传入运算的参数必须是整数 -- dealNumberPoint")
	  }
	  if(pointLength < 0 || Number(pointLength) % 1 !== 0){
	    throw new Error("小数位数的值要大于等于0的整数")
	  }
	  // 判断数据是正数还是负数
	  let flag = val < 0 ? true : false;
	  // 取绝对值进行运算
	  val = String(Math.abs(val));
	  // 小数位数与当前的整数的位数长度差,如果大于零就是需要补零操作
	  // 2021.11.08 修改 这里之前写成了 - val,导致了错误
	  let minusZero = pointLength - String(val).length;
	  if(minusZero >= 0){
	    for(let i = 0; i < minusZero; i++){
	      val = '0' + val;
	    }
	    // 循环完了之后加上 '0.'
	    val = '0.' + val;
	  }else{
	    if(val.length > 1){
	      let pointPosition = val.length - pointLength;
	      // 小数点的前部分
	      let start = val.slice(0,pointPosition);
	      // 小数点的后部分
	      let end = val.slice(pointPosition);
	      // 合成
	      val = start+'.'+end;
	    }
	  }
	  // 考虑正、负
	  return  flag ? -Number(val) : Number(val);
	}

由于除法容易出现除不尽的情况,于是做了保留小数的处理,最多保留两位小数。函数中增加的注释是自己的思路习惯,作为记录。

以上方法作为此次处理的记录,后续或许会继续优化这个方法
2021-12-10 更新一次 – 修复类似 0.8 * 1 计算得出 8 这样的错误