Browse Source

add 重建 计算模块

dev v0.0.1
lucas 1 month ago
parent
commit
e0b014abf2
  1. 9
      LICENSE
  2. 2
      README.md
  3. 109
      calc_test.go
  4. 276
      formulaTemplateCalculate.go
  5. 14
      go.mod
  6. 12
      go.sum
  7. 115
      valueHelper.go

9
LICENSE

@ -0,0 +1,9 @@
MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

2
README.md

@ -1,3 +1,3 @@
# common_calc
原有 gitea 仓库损坏重建
通用计算模块

109
calc_test.go

@ -0,0 +1,109 @@
package common_calc
import (
"github.com/stretchr/testify/assert"
"log"
"math"
"testing"
)
func Test_formulaTemplate_demo1(t *testing.T) {
//公式表达式计算
formula := "(1.1*2 + 5/2)+ 2.4/2 + 0.8"
result := CalculateFormula(formula)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo2(t *testing.T) {
//公式表达式计算
formula := "2² + 0.8"
result := CalculateFormula(formula)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo3(t *testing.T) {
//公式表达式计算
formula := "(1.1*2 + 5/2)+ 2.4/2² + 0.8"
result := CalculateFormula(formula)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo4(t *testing.T) {
//公式表达式计算 √
formula := "√4 + 0.8"
result := CalculateFormula(formula)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo5(t *testing.T) {
//公式表达式计算 √
formula := "√4+12 + 0.8"
result := CalculateFormula(formula)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo6(t *testing.T) {
//公式表达式计算 √
formula := "3*√4*5 + 0.8"
result := CalculateFormula(formula)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo7(t *testing.T) {
//公式表达式计算 √
formula := "3*√(2*8) + 0.8"
result := CalculateFormula(formula)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo8(t *testing.T) {
//公式表达式计算 √
formula := "2*[1+2]+0.5"
result := CalculateFormula(formula)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
formula2 := "2*[3*(1+2)]+0.5"
result2 := CalculateFormula(formula2)
log.Printf("表达式 %s =>计算结果=%v", formula2, result2)
}
func Test_formulaTemplate_demo9(t *testing.T) {
formula := "sin(30*π/180)+cos(60*π/180)"
result := CalculateFormula(formula)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo10(t *testing.T) {
formula := "[sin(30*π/180)+1]+sin(60*π/180)"
result := CalculateFormula(formula)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo11(t *testing.T) {
formula := "[1+2³+3]"
result := CalculateFormula(formula)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo12(t *testing.T) {
formula := "1+2*π"
result := CalculateFormula(formula)
assert.Equal(t, result, 1+2*math.Pi)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo12_2(t *testing.T) {
formula := "1+2π"
result := CalculateFormula(formula)
assert.Equal(t, result, 1+2*math.Pi)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
func Test_formulaTemplate_demo13(t *testing.T) {
formula := "3^2+1"
result := CalculateFormula(formula)
assert.Equal(t, result, 10.0)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}
// -0.37-0
func Test_formulaTemplate_demo14(t *testing.T) {
formula := "-0.37-0"
result := CalculateFormula(formula)
assert.Equal(t, result, -0.37)
log.Printf("表达式 %s =>计算结果=%v", formula, result)
}

276
formulaTemplateCalculate.go

@ -0,0 +1,276 @@
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
}

14
go.mod

@ -0,0 +1,14 @@
module gitea.anxinyun.cn/container/common_calc
go 1.22.0
require (
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.9.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

12
go.sum

@ -0,0 +1,12 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

115
valueHelper.go

@ -0,0 +1,115 @@
package common_calc
import (
"crypto/md5"
"github.com/google/uuid"
"log"
"math"
"sort"
"strconv"
)
// Decimal float64 保留小数点后位数
// value float64 浮点数
// prec int 需保留小数点后的位数
func Decimal(value float64, prec int) float64 {
value, _ = strconv.ParseFloat(strconv.FormatFloat(value, 'f', prec, 64), 64)
return value
}
// 和scala 计算不一致 注意!
func UUIDFromString(raw string) string {
bys := []byte(raw)
uid := uuid.NewMD5(uuid.Nil, bys)
log.Println(uid.String(), uid.Version())
uid = uuid.NewMD5(uuid.NameSpaceURL, bys)
log.Println(uid.String(), uid.Version())
uid = uuid.NewMD5(uuid.NameSpaceX500, bys)
log.Println(uid.String(), uid.Version())
uid = uuid.NewMD5(uuid.NameSpaceDNS, bys)
log.Println(uid.String(), uid.Version())
uid = uuid.NewMD5(uuid.NameSpaceOID, bys)
log.Println(uid.String(), uid.Version())
return uid.String()
}
func NameUUIDFromString(raw string) string {
sb := md5.Sum([]byte(raw))
sb[6] &= 0x0f
sb[6] |= 0x30
sb[8] &= 0x3f
sb[8] |= 0x80
uid, err := uuid.FromBytes(sb[:])
if err != nil {
log.Printf("uid 错误异常=%x", err.Error())
}
return uid.String()
}
func MinMax(input []float64) (float64, float64) {
sort.Float64s(input)
return input[0], input[len(input)-1]
}
func AbsMax(input []float64) float64 {
sort.Float64s(input)
return max(math.Abs(input[0]), math.Abs(input[len(input)-1]))
}
func MeanSqrt(data []float64) float64 {
var sumSquares float64
for _, v := range data {
sumSquares += v * v
}
return math.Sqrt(sumSquares / float64(len(data)))
}
func GetAvg(dataArray []float64) float64 {
sum := 0.0
for _, f := range dataArray {
sum += f
}
return sum / float64(len(dataArray))
}
func GetMedian(dataArray []float64) float64 {
n := len(dataArray)
sort.Float64s(dataArray)
if n%2 == 1 {
// 如果切片元素个数为奇数,中位数即为中间的元素
return dataArray[n/2]
} else {
// 如果元素个数为偶数,中位数为中间两个元素的平均值
return (dataArray[n/2-1] + dataArray[n/2]) / 2.0
}
}
// GetVariance calculates the unbiased population variance from the provided samples as an unsorted array.
// On a dataset of size N, it will use an N-1 normalizer (Bessel's correction).
// Returns NaN if data has less than two entries or if any entry is NaN.
// 方差
// Translated from Math.Net
// Source code at https://github.com/mathnet/mathnet-numerics/blob/master/src/Numerics/Statistics/ArrayStatistics.cs
func GetVariance(samples []float64) float64 {
if len(samples) <= 1 {
return math.NaN()
}
var variance float64
t := samples[0]
for i := 1; i < len(samples); i++ {
t += samples[i]
diff := float64((i+1)*int(samples[i])) - t
variance += (diff * diff) / (float64(i+1) * float64(i))
}
return variance / float64(len(samples)-1)
}
func StandardDeviation(samples []float64) float64 {
return math.Sqrt(GetVariance(samples))
}
func MeanStandardDeviation(samples []float64) (float64, float64) {
return GetAvg(samples), StandardDeviation(samples)
}
Loading…
Cancel
Save