【JS】数值精度缺失问题解决方案

方法一:
保留字符串类型,传给后端

方法二:
如果涉及到计算,用以下方法

// 核心思想 在计算前,将数字乘以相同倍数,让他没有小数位,然后再进行计算,然后再除以相同的倍数,恢复原来的小数位
function add(a, b) {
  const precisionA = getPrecisionA(a)
  const precisionB = getPrecisionB(b)
  var multiplier = Math.pow(10, Math.max(precisionA, precisionB))
  return (a * multiplier + b * multiplier) / multiplier
}

function getPrecisionA() {
  if (num.toString().indexOf('e') !== -1) {
    var precision = num.toString().split('e-')[1]
    return parseInt(precision, 10)
  }

  var decimalPart = num.toString().split('.')[1]
  return decimalPart ? decimalPart.length : 0
}

以下是完整函数:

/**
 * 把递归操作扁平迭代化
 * @param {number[]} arr 要操作的数字数组
 * @param {function} operation 迭代操作
 * @private
 */
function iteratorOperation(arr,operation){
const [num1,num2,...others]=arr;
let res=operation(num1,num2)
other.forEach((num)=>{
res=operation(res,num)
})
return res
}

/**
 * 返回小数部分的长度
 * @private
 * @param {*number} num Input number
 */
 function digitLength(num){
 // 指数部分通常是用字母 "e" 或 "E" 后跟一个整数表示
 const eSplit=num.toString().spilt(/[eE]/);
 const len=(eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0);
 return len>0?len:0;
}

/**
 * 把小数转成整数,如果是小数则放大成整数
 * @private
 * @param {*number} num 输入数
 */
function float2Fixed(num){
if(num.toString().indexOf('e')===-1){
return Number(num.toString().replace('.',''))
}
const dLen = digitLength(num);
  return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num);
}

/**
 * 把错误的数据转正
 * @private
 * @example strip(0.09999999999999998)=0.1
 */
 function strip(num,precision=15){
 // toPrecision将数字转换为具有指定有效数字位数的字符串表示形式
 // parseFloat 将字符串转为浮点数
  return +parseFloat(Number(num).toPrecision(precision));
 }

/**
 * 高精度乘法
 */
 function times(...nums){
   if (nums.length > 2) {
    return iteratorOperation(nums, times);
  }
  const [num1, num2] = nums;
  const num1Changed = float2Fixed(num1);
  const num2Changed = float2Fixed(num2);
  const baseNum = digitLength(num1) + digitLength(num2);
  const leftValue=num1Changed * num2Changed
  // 并返回底数的指数次幂
  return  leftValue/ Math.pow(10, baseNum);
  
 }

/**
 * 高精度加法
 */
 function plus(...nums){
if(nums.length>2){
return iteratorOperation(nums, plus)
}
const [num1,num2]=nums
// 取最大的小数位
// Math.pow 函数计算以 10 为底、指数为两个数字小数部分长度的最大值的幂次方
const baseNum=Math.pow(10,Math.max(digitLength(num1), digitLength(num2)))
// 把小数转为整数再计算
 return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
}

/**
 * 高精度减法
 * @export
 */
 function minus(...nums) {
  if (nums.length > 2) {
    return iteratorOperation(nums, minus);
  }

  const [num1, num2] = nums;
  const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
  return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
}

/**
 * 高精度除法
 * @export
 */
function divide(...nums) {
  if (nums.length > 2) {
    return iteratorOperation(nums, divide);
  }

  const [num1, num2] = nums;
  const num1Changed = float2Fixed(num1);
  const num2Changed = float2Fixed(num2);
  checkBoundary(num1Changed);
  checkBoundary(num2Changed);
  // 重要,这里必须用strip进行修正
  return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1))));
}
  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值