You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
6.5 KiB
276 lines
6.5 KiB
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
|
|
}
|
|
|