上图看效果
如果小数直接做运算,很容易发生如上图的精度丢失的情况,这个问题还是百度:
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 这样的错误