基于bignumber.js简单解决js的精度问题_封装bignumber.js_js中使用简单运算
2024-11-20 15:53:37
一种利用bignumber.js库处理JavaScript中常见浮点数精度问题的简便方法。详细介绍了如何在实际编程中应用以解决精度问题,帮助开发者在日常开发中更有效的处理精度问题。
430

在 JavaScript 中进行十进制运算时,由于 JavaScript 内置的数字类型是双精度浮点数,可能会遇到精度丢失的问题。对于需要更高精度的金融计算、科学计算或其他特殊需求,BigNumber.js 可以提供更好的解决方案

BigNumber.js 是一个强大而灵活的库,用于进行高精度十进制运算。通过使用 BigNumber 对象,你可以避免 JavaScript 中浮点数精度丢失的问题,进行更准确的数学计算。根据你的具体需求,你可以进一步探索 BigNumber.js 的其他功能和方法。

  • 安装和引入 BigNumber.js:

你可以通过 npm 或直接下载 BigNumber.js 文件来使用它。在代码中使用 require()<script> 标签来引入库。

npm install bignumber.js --save
  • 创建和使用 BigNumber 对象:

使用 BigNumber() 构造函数可以创建一个 BigNumber 对象。你可以通过传递一个数字或字符串来初始化这个对象。

import BigNumber from "bignumber.js";
  • 基本算术运算:

BigNumber 对象支持常见的算术运算,如加法、减法、乘法和除法。你可以使用相应的方法来执行这些运算。

// 加法
BigNumber(1).plus(0,1).toString(); 
// 减法
BigNumber(1).mimus(1).toString(); 
// 乘法
BigNumber(1).times(2).toString(); 
// 除法
BigNumber(1).div(2).toString(); 
// 取余
BigNumber(1).mod(3).toString(); 
// 绝对值
BigNumber(1).abs().toString(); 
  • 精度控制:

BigNumber.js 允许你设置精度参数,以控制运算的精度。

  • 比较和相等性检查:

你可以使用 compareTo() 方法来比较两个 BigNumber 对象的大小,也可以使用 equals() 方法来检查两个 BigNumber 对象是否相等。

  • 其他方法和功能:

BigNumber.js 还提供了其他一些方法,如取余数、取整、小数点移动等。

但是使用中方法比较复杂,现在引用了网友的字符串运算符方法

https://www.npmjs.com/package/calc-number
import BigNumber from "bignumber.js";
export function fnBigNumber(runStr) {
  let allMethod={
    sin:(data)=>Math.sin(data[0]),
    cos:(data)=>Math.cos(data[0]),
    tan:(data)=>Math.tan(data[0]),
    atan:(data)=>Math.atan(data[0]),
  }
  class runObj {
    constructor(type, data) {
      this.type = type;
      this.data = data.map(v => {
        if (v instanceof runObj) {
          return v.run();
        } else if (v instanceof Array) {
          let runObjItem = new runObj(v[1], [v[0], v[2]]);
          return runObjItem.run();
        } else {
          return v;
        }
      });
    }
    run() {
      if (this.type === '+') {
        const temp = new BigNumber(this.data[0])
        return temp.plus(this.data[1]).toNumber()
      } else if (this.type === '-') {
        const temp = new BigNumber(this.data[0])
        return temp.minus(this.data[1]).toNumber()
      } else if (this.type === '*') {
        const temp = new BigNumber(this.data[0])
        return temp.multipliedBy(this.data[1]).toNumber()
      } else if (this.type === '/') {
        const temp = new BigNumber(this.data[0])
        return temp.div(this.data[1]).toNumber()
      } else if (this.type === '**') {
        const temp = new BigNumber(this.data[0])
        return temp.pow(this.data[1]).toNumber()
      } else if (this.type === 'number') {
        return this.data[0];
      } else if (allMethod[this.type]) {
        return allMethod[this.type](this.data);
      }else{
        throw new Error('运算类型不存在' + this.type)
      } 
    }
  }
  const allKeyWord = [
    Object.keys(allMethod),
    ['**'],
    ['*', '/'],
    ['+', '-'],
  ];// 以优先级排列
 
  // 第一步分词
  let resultArr = _split(runStr); // '1+2'    =>   ['1', '+', '2']

  let aCorrectArr=[];
  let _flag=false;//是否是两个运算符想家
  resultArr.forEach((e,i)=>{
    if(['+', '-', '*', '/'].includes(e)){
      if(_flag){
        //如果_flag为true 说明上一个也是运算符,则也需要手动push
        aCorrectArr.push("0");
        _flag=false;
      }
      if(i==0){
         //如果第一个就是运算符 需要手动加一个
           aCorrectArr.push("0");
      }
      //1.如果是运算符就改变flag
      _flag=true;
    }else{
      _flag=false
    }
    aCorrectArr.push(e)
  });
  if(['+', '-', '*', '/'].includes(aCorrectArr[aCorrectArr.length-1])){
    //如果最后一个是运算符
    aCorrectArr.push("0")
  }

  let slip = 0;
  let runObjItem = arrayToRunObjType(aCorrectArr, []);
  return runObjItem.run();
  function _split(runStr) {
    let resultArr = [];
    let split = 0;
    while (split < runStr.length) {
      const match = Object.keys(allMethod).find(v => v === runStr.slice(split, split + v.length));
      if (match) {
        resultArr.push(runStr.slice(split, split + match.length))
        split += 3;
      } else if (['**'].includes(runStr.slice(split, split + 2))) {
        resultArr.push(runStr.slice(split, split + 2))
        split += 2;
      } else if (['+', '-', '*', '/', '(', ')'].includes(runStr[split])) {
        resultArr.push(runStr[split]);
        split++;
      } else if (runStr[split].match(/d/)) {
        let numberStr = runStr[split];
        for (let i = split + 1; i < runStr.length; i++) {
          if (runStr[i].match(/d/) || runStr[i] === '.') {
            numberStr += runStr[i];
            split++;
          } else {
            break;
          }
        }
        resultArr.push(numberStr)
        split++;
      } else {
        split++;
      }
    }
    return resultArr
  }
  function arrayToRunObjType(resultArr, endKeyWord = []) {
    let temp = [[]];
 
    for (; slip < resultArr.length; slip++) {
      const word = resultArr[slip]
      if (endKeyWord.includes(word)) {
        break;
      }
      if (word.match(/d+/)) {
        temp[temp.length - 1].push(word);
      } else if (word === '(') {
        slip++;
        const result = arrayToRunObjType(resultArr, [')']);
        if (resultArr[slip] !== ')') {
          throw new Error('结构错误')
        }
        temp[temp.length - 1].push(result);
      } else if (Object.keys(allMethod).includes(word)) {
        slip += 2;
        const nextObj = arrayToRunObjType(resultArr, [')'])
        if (temp[temp.length - 1].length === 0) {
          temp[temp.length - 1].push(nextObj)
          temp[temp.length - 1].push(word)
        } else {
          temp[temp.length - 1].push([nextObj, word])
        }
        if (resultArr[slip] !== ')') {
          throw new Error('结构错误')
        }
      } else if (['**', '*', '/', '+', '-'].includes(word)) {
        if (temp[temp.length - 1].length === 1) {
          temp[temp.length - 1].push(word);
        } else {
          const preview = temp[temp.length - 1];
          const thisIndex = allKeyWord.findIndex(v => v.includes(word));
          const preIndex = allKeyWord.findIndex(v => v.includes(preview[1]))
          const allNextKeyword = []
          allKeyWord.slice(thisIndex + 1).forEach(v => {
            v.forEach(vv => {
              allNextKeyword.push(vv)
            })
          })
          if (thisIndex < preIndex || (['**'].includes(word) && thisIndex === preIndex)) { // 本优先级更高进行优先级抢夺,**连自己也会抢夺
            slip++;
            const nextObj = arrayToRunObjType(resultArr, allNextKeyword)
            slip--;
            preview[preview.length - 1] = [
              preview[preview.length - 1],
              word,
              nextObj,
            ]
          } else {
            temp[temp.length - 1] = [temp[temp.length - 1]]
            temp[temp.length - 1].push(word);
          }
        }
      } else {
        throw new Error('分词失败' + word)
      }
    }
    if (temp[temp.length - 1].length === 1) {
      // 1+2*3/2+2*4
      // 1+(2*3/2)+(2*4)
      if (typeof temp[temp.length - 1][0] === 'string') {
        temp[temp.length - 1] = parseFloat(temp[temp.length - 1][0])
      } else {
        temp[temp.length - 1] = temp[temp.length - 1][0]
      }
    }
 
    if (temp.length === 1) {
      temp = temp[0]
    }
    if (typeof temp !== 'number') {
   
    }
  
    let runObjItem;
    if (typeof temp === 'number') {
      runObjItem = new runObj('number', [temp]);
    } else if (['+', '-', '*', '/', '**'].includes(temp[1])) {
      runObjItem = new runObj(temp[1], [temp[0], temp[2]]);
    } else if (Object.keys(allMethod).includes(temp[1])) {
      runObjItem = new runObj(temp[1], [temp[0]]);
    } else {
      throw new Error('结构不存在' + temp[1])
    }
    return runObjItem;
  }
  
}
  • 调用方法:
fnBigNumber('1-1')