结果
calc('0.23*-0.03+(4*0.2)')
=>0.7931
代码
//加|减|乘|除 浮点运算
const floatMulti = (a, b) => {let m = 0, s1 = a.toString(), s2 = b.toString(), s1l = s1.split('.')[1], s2l = s2.split('.')[1]m = (s1l ? s1l.length : 0) + (s2l ? s2l.length : 0)return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m)
}
const floatSub = (a, b) => {let m = 0, s1 = a.toString(), s2 = b.toString(), s1l = s1.split('.')[1], s2l = s2.split('.')[1]m = Math.max((s1l ? s1l.length : 0) ,(s2l ? s2l.length : 0))return(Number(s1)*Math.pow(10, m) - Number(s2)*Math.pow(10, m)) / Math.pow(10, m)
}
const floatAdd = (a, b) => {let m = 0, s1 = a.toString(), s2 = b.toString(), s1l = s1.split('.')[1], s2l = s2.split('.')[1]m = Math.max((s1l ? s1l.length : 0) ,(s2l ? s2l.length : 0))return (Number(s1)*Math.pow(10, m) + Number(s2)*Math.pow(10, m)) / Math.pow(10, m)
}
const floatDev = (a, b) => {let m = 0, s1 = a.toString(), s2 = b.toString(), s1l = s1.split('.')[1], s2l = s2.split('.')[1]m = (s1l ? s1l.length : 0) + (s2l ? s2l.length : 0)return (Number(s1.replace('.', '')) / Number(s2.replace('.', ''))) / Math.pow(10, m)
}
/*
* 浮点运算式
* */
export const calc = (() => {//一般的算数表达式即为中缀表达式// let str = "1+((2 + 3 ) * 4 ) - 5 ";//运算符权重const sdw = { '+': 1, '-': 1, '*': 2, '/': 2 };//循环安全数let saveCircle = 137;/***判断是 运算符|操作数|括号* 【-】字符前面有任何其他运算符则本身被当作数字一部分*@return 1:操作数 2:括号 0:运算符*/const typeChk = (d) => /^-*[\.\d]+$/.test(d) ? 1 : (/[\(\)]/.test(d) ? 2 : 0)/** 操作符操作方法* */const handle = { '+': floatAdd, '-': floatSub, '*': floatMulti, '/': floatDev }/*** 处理输入* 1) 替换中英括号* 2) 去除空格* 3) 小数点前后非数字,则去除* 3) 小数点之前为空,后面为数字则默认前面添加0* 4) 返回字符分割*/const dealStr = (str) => str//替换左右括号.replace(/(/, '(').replace(/)/, ')')//替换空字符.replace(/[^\d\.\(\)\+\*\/-]/g, '')//替换-// .replace(/(\d)-/g,'$1+-')// .replace(/--/g,'+')// .replace(/-\(/g,'-1*(')// .replace(/-/g,'+-1*').match(/(((?<=[^\d])-[\.\d]+)+)|(^-[\.\d]+)|([\d\.]+)|([\(\)\+\*\/-])/g)//中缀表达式转前缀表达式并计算return (str) => {let operand = [];//操作数栈let operator = [];//操作符栈//去空格(主要是避免数字被空格拆分),分割成数组let arr = dealStr(str);// log(arr)//按照规则入栈let type;for (let i = arr.length - 1, curr; (curr = arr[i]) != null; i--) {type = typeChk(curr);if (type === 1) {operand.push(+curr)} else if (type === 0) {let si = 0, stackTop;while (true && si++ < saveCircle) {stackTop = operator[operator.length - 1];if (stackTop == null || stackTop === ')') {operator.push(curr);break;} else if (sdw[curr] >= sdw[stackTop]) {operator.push(curr);break;} else {operand.push(operator.pop())}}} else {if (curr === ')') {operator.push(curr)}else{let s, si = 0;while (s !== ')' && si++ < saveCircle) {s = operator.pop();if (s === ')') breakoperand.push(s)}}}}//将操作符栈中剩下的弹出压入操作数栈// operand = [...operand, ...operator.reverse()];// num => ["5", "4", "3", "2", "+", "×", "1", "+", "-"]//"1+((2 + 3 ) × 4 ) - 5 "let prevStack = [...operator, ...operand.reverse()];// ["-", "+", "1", "×", "+", "2", "3", "4", "5"]//计算let resultStack = [], left, right;for (let i = prevStack.length - 1, curr; (curr = prevStack[i]) != null; i--) {if (typeChk(curr)) {//是操作数resultStack.push(curr)} else {//是操作符left = resultStack.pop();right = resultStack.pop();// console.log(left,right,curr)resultStack.push(handle[curr](left, right))}}// log(prevStack);//log(resultStack);return resultStack.length != 1 ? '表达式有误' : resultStack[0]}
})()