计算器

65 阅读2分钟

解决直接计算会精度丢失的问题,

import Decimal from 'decimal.js';
import _ from 'lodash';


export const thousandBitSeparator=(num)=> {
    return num && num
        .toString()
        .replace(/(\d)(?=(\d{3})+\.)/g, function($0, $1) {
            return $1 + ",";
        });
}


// 计算器
// str 即为input输入的数据
function sliceCountStr(str) {
    const arr: string[] = []; // 记录分割计算表达式
    for (let i = 0; i < str.length; i++) {
        const item = str.charAt(i);
        if(item === ' ') continue
        let num = item;
        if (/[\d|\.]/.test(item)) {
            let j = i + 1;
            for (; j < str.length; j++) {
                const otherItem = str.charAt(j);
                if (!/[\d|\.]/.test(otherItem)) {
                    break;
                }
            }
            num = str.slice(i, j);
            i = j - 1;
            num = +num;
        }
        arr.push(num);
    }
    return arr;
}
// arr 即为上一步处理好的数组
export function countHandle(formula) {
    const arr = sliceCountStr(formula);
    const charArr: string[] = [];
    const numArr: string[] = [];
    for (let i = 0; i < arr.length; i++) {
        if (_.isNumber(arr[i])) {
            numArr.push(arr[i]);
        } else {
            if (charArr.length) {
                // 步骤1
                // 如果当前的运算符的优先级比栈顶的优先级低或相等,就说明需要把前面的值全部计算好
                // 存储运算符的栈要一直出栈,直到栈为空或当前的字符的优先级比栈顶的优先级高
                while (isPop(arr[i], charArr[charArr.length - 1])) {
                    const t2 = numArr.pop();
                    const t1 = numArr.pop();
                    const char = charArr.pop();
                    handleCalculation(numArr, t1, t2, char);
                }
                // 当前运算符为右括号
                if (arr[i] === ')') {
                    // 取栈顶运算符
                    let st: string | undefined = charArr[charArr.length - 1];
                    // 步骤2
                    // 遇到右括号也要一直出栈,直到遇到左括号
                    while (st !== '(') {
                        let t1;
                        let t2;
                        const char = charArr.pop();
                        if (char !== '(') {
                            t2 = numArr.pop();
                            t1 = numArr.pop();
                            handleCalculation(numArr, t1, t2, char);
                        }
                        st = char;
                    }
                }
                // 运算符不为右括号
                if (arr[i] !== ')') {
                    charArr.push(arr[i]);
                }
            } else {
                // 步骤3
                // 运算符栈为空,直接入栈
                charArr.push(arr[i]);
            }
        }
    }
    // 步骤4
    // 最后运算符栈如果还有字符,要一直出栈直到为空
    while (charArr.length) {
        const t2 = numArr.pop();
        const t1 = numArr.pop();
        const char = charArr.pop();
        handleCalculation(numArr, t1, t2, char);
    }
    return numArr[0];
}

// 基本加减乘除运算处理
function handleCalculation(numArr, num1, num2, char) {
    const _num1 = new Decimal(num1);
    let result;
    switch (char) {
        case '+':
            result = _num1.plus(num2);
            break;
        case '-':
            result = _num1.minus(num2);
            break;
        case '*':
            result = _num1.times(num2);
            break;
        case '/':
            result = _num1.div(num2);
            break;
        default: console.error('运算符错误',char);
            break;
    }
    result && numArr.push(result.toFixed(2));
}
// 判断运算符的优先级,是否出栈进行计算
function isPop(char1, char2) {
    // 运算符栈为空
    if (!char2) {
        return false;
    }
    // 运算符优先级相同
    if (['+', '-'].includes(char1) && ['+', '-'].includes(char2)) {
        return true;
    }
    // 前者运算符优先级比后者低
    if (['+', '-'].includes(char1) && ['*', '/'].includes(char2)) {
        return true;
    }
    // 运算符优先级相同
    if (['*', '/'].includes(char1) && ['*', '/'].includes(char2)) {
        return true;
    }
    // 前者运算符优先级比后者高
    if (['*', '/'].includes(char1) && ['+', '-'].includes(char2)) {
        return false;
    }
}