package common_calc import ( "errors" "fmt" "log" "math" "strconv" "strings" "unicode" ) func CalculateFormula(formulaExpression string) float64 { //log.Printf("计算 %s\n", formulaExpression) formulaExpression = strings.Replace(formulaExpression, " ", "", -1) postfix, err := infix2ToPostfix(formulaExpression) if err != nil { log.Printf("计算异常[%s]=>%s", err.Error(), formulaExpression) return -255 } return calculatePostfix(postfix) } func calculatePostfix(postfix []string) float64 { stack := Stack{} fixLen := len(postfix) for i := 0; i < fixLen; i++ { nextChar := postfix[i] // 数字:直接压栈 if _, err := strconv.ParseFloat(nextChar, 64); err == nil { stack.Push(nextChar) } else { if nextChar == "π" { stack.Push(fmt.Sprintf("%v", math.Pi)) } else { switch nextChar { case "sin": // 操作符:取出数字计算值,再将结果压栈 num1, _ := strconv.ParseFloat(stack.Pop(), 64) stack.Push(fmt.Sprintf("%v", math.Sin(num1))) case "cos": // 操作符:取出数字计算值,再将结果压栈 num1, _ := strconv.ParseFloat(stack.Pop(), 64) stack.Push(fmt.Sprintf("%v", math.Cos(num1))) case "√": // 操作符:取出数字计算值,再将结果压栈 num1, _ := strconv.ParseFloat(stack.Pop(), 64) stack.Push(fmt.Sprintf("%v", math.Sqrt(num1))) case "²": // 操作符:取出数字计算值,再将结果压栈 num1, _ := strconv.ParseFloat(stack.Pop(), 64) stack.Push(fmt.Sprintf("%v", math.Pow(num1, 2))) case "³": // 操作符:取出数字计算值,再将结果压栈 num1, _ := strconv.ParseFloat(stack.Pop(), 64) stack.Push(fmt.Sprintf("%v", math.Pow(num1, 3))) case "+": // 操作符:取出两个数字计算值,再将结果压栈 num1, _ := strconv.ParseFloat(stack.Pop(), 64) num2, _ := strconv.ParseFloat(stack.Pop(), 64) stack.Push(fmt.Sprintf("%v", num2+num1)) case "-": num1, _ := strconv.ParseFloat(stack.Pop(), 64) num2, _ := strconv.ParseFloat(stack.Pop(), 64) stack.Push(fmt.Sprintf("%v", num2-num1)) case "*": num1, _ := strconv.ParseFloat(stack.Pop(), 64) num2, _ := strconv.ParseFloat(stack.Pop(), 64) stack.Push(fmt.Sprintf("%v", num2*num1)) case "/": num1, _ := strconv.ParseFloat(stack.Pop(), 64) num2, _ := strconv.ParseFloat(stack.Pop(), 64) stack.Push(fmt.Sprintf("%v", num2/num1)) case "^": // 操作符:取出两个数字计算值,再将结果压栈 num2, _ := strconv.ParseFloat(stack.Pop(), 64) num1, _ := strconv.ParseFloat(stack.Pop(), 64) stack.Push(fmt.Sprintf("%v", math.Pow(num1, num2))) } } } } result, _ := strconv.ParseFloat(stack.Top(), 64) return result } // 中缀表达式转后缀表达式 func infix2ToPostfix(expStr string) ([]string, error) { stack := Stack{} postfix := make([]string, 0) //-1.1-2 => 0-1.1-2 //-1.1*2+1 => 0-1.1*2+1 if strings.HasPrefix(expStr, "-") { expStr = fmt.Sprintf("0%s", expStr) } exp := []rune(expStr) expLen := len(exp) // 遍历整个表达式 itemStr := "" for i := 0; i < expLen; i++ { char := string(exp[i]) itemStr += char switch itemStr { case " ": continue case "[": // 左括号直接入栈 stack.Push("[") case "]": // 右括号则弹出元素直到遇到左括号 for !stack.IsEmpty() { preChar := stack.Top() if preChar == "[" { stack.Pop() // 弹出 "(" break } postfix = append(postfix, preChar) stack.Pop() } case "(": // 左括号直接入栈 stack.Push("(") case ")": // 右括号则弹出元素直到遇到左括号 for !stack.IsEmpty() { preChar := stack.Top() if preChar == "(" { stack.Pop() // 弹出 "(" break } postfix = append(postfix, preChar) stack.Pop() } case "π": postfix = append(postfix, "π") // 数字则直接输出 case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".": j := i digit := "" for ; j < expLen && (unicode.IsDigit(exp[j]) || "." == string(exp[j])); j++ { digit += string(exp[j]) } postfix = append(postfix, digit) //支持 如2π这种写法 if j < expLen && "π" == string(exp[j]) { postfix = append(postfix, "π", "*") j++ } i = j - 1 // i 向前跨越一个整数,由于执行了一步多余的 j++,需要减 1 default: //非操作符继续 拼接 用以支持多字符操作符如sin , cos if isOp := isOperator(itemStr); !isOp { if i+1 == expLen || len(itemStr) > 5 { return postfix, errors.New(fmt.Sprintf("不支持的无效的公式片段 %s", itemStr)) } if unicode.IsDigit(exp[i+1]) { return postfix, errors.New(fmt.Sprintf("不支持的操作符 %s", itemStr)) } continue } // 操作符:遇到高优先级的运算符,不断弹出,直到遇见更低优先级运算符 for !stack.IsEmpty() { top := stack.Top() if (top == "(" || top == "[") || isLower(top, itemStr) { break } postfix = append(postfix, top) stack.Pop() } // 低优先级的运算符入栈 stack.Push(itemStr) } itemStr = "" } // 栈不空则全部输出 for !stack.IsEmpty() { postfix = append(postfix, stack.Pop()) } return postfix, nil } // (12.3+0.1)*2.0+2 // 12.30.1+2.0*2+ // 后缀式计算 var operatorMap = []string{"√", "²", "³", "+", "-", "*", "/", "sin", "cos", "^"} // 优先级定义 var operatorPriority = map[string]uint16{ "+": 0, "-": 0, "*": 1, "/": 1, "√": 2, "^": 6, "sin": 7, "cos": 7, "²": 8, "³": 8, "[": 9, "(": 10, } func isOperator(op string) bool { for _, s := range operatorMap { if op == s { return true } } return false } // 比较运算符栈栈顶 top 和新运算符 newTop 的优先级高低 func isLower(top string, newTop string) bool { // 注意 a + b + c 的后缀表达式是 ab + c +,不是 abc + + Priority := operatorPriority[newTop] > operatorPriority[top] return Priority //switch top { //case "+", "-": // if newTop == "*" || newTop == "/" { // return true // } //case "(": // return true //} //return false } type Stack struct { data [100]string top int } func (s *Stack) Push(d string) { s.data[s.top] = d s.top++ } func (s *Stack) Pop() (d string) { if s.top == 0 { //err = fmt.Errorf("stack is empty") } s.top-- d = s.data[s.top] return } func (s *Stack) Top() (d string) { if s.top == 0 { //err = fmt.Errorf("stack is empty") } d = s.data[s.top-1] return } func (s *Stack) IsEmpty() bool { return s.top == 0 }