Fix hyperbolic cotangent calculation incorrect and unit test

This commit is contained in:
xuri 2021-01-31 13:15:10 +08:00
parent 76c72e0a30
commit 4ac32278ff
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
2 changed files with 162 additions and 137 deletions

179
calc.go
View File

@ -344,8 +344,7 @@ func newErrorFormulaArg(formulaError, msg string) formulaArg {
// //
func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error) { func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error) {
var err error var err error
opdStack, optStack, opfStack, opfdStack, opftStack := NewStack(), NewStack(), NewStack(), NewStack(), NewStack() opdStack, optStack, opfStack, opfdStack, opftStack, argsStack := NewStack(), NewStack(), NewStack(), NewStack(), NewStack(), NewStack()
argsList := list.New()
for i := 0; i < len(tokens); i++ { for i := 0; i < len(tokens); i++ {
token := tokens[i] token := tokens[i]
@ -359,6 +358,7 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
// function start // function start
if token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStart { if token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStart {
opfStack.Push(token) opfStack.Push(token)
argsStack.Push(list.New().Init())
continue continue
} }
@ -396,7 +396,7 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
if result.Type == ArgUnknown { if result.Type == ArgUnknown {
return efp.Token{}, errors.New(formulaErrorVALUE) return efp.Token{}, errors.New(formulaErrorVALUE)
} }
argsList.PushBack(result) argsStack.Peek().(*list.List).PushBack(result)
continue continue
} }
} }
@ -417,7 +417,7 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
opftStack.Pop() opftStack.Pop()
} }
if !opfdStack.Empty() { if !opfdStack.Empty() {
argsList.PushBack(formulaArg{ argsStack.Peek().(*list.List).PushBack(formulaArg{
String: opfdStack.Pop().(efp.Token).TValue, String: opfdStack.Pop().(efp.Token).TValue,
Type: ArgString, Type: ArgString,
}) })
@ -431,7 +431,7 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
// current token is text // current token is text
if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeText { if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeText {
argsList.PushBack(formulaArg{ argsStack.Peek().(*list.List).PushBack(formulaArg{
String: token.TValue, String: token.TValue,
Type: ArgString, Type: ArgString,
}) })
@ -450,26 +450,26 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
// push opfd to args // push opfd to args
if opfdStack.Len() > 0 { if opfdStack.Len() > 0 {
argsList.PushBack(formulaArg{ argsStack.Peek().(*list.List).PushBack(formulaArg{
String: opfdStack.Pop().(efp.Token).TValue, String: opfdStack.Pop().(efp.Token).TValue,
Type: ArgString, Type: ArgString,
}) })
} }
// call formula function to evaluate
arg := callFuncByName(&formulaFuncs{}, strings.NewReplacer( arg := callFuncByName(&formulaFuncs{}, strings.NewReplacer(
"_xlfn", "", ".", "").Replace(opfStack.Peek().(efp.Token).TValue), "_xlfn", "", ".", "").Replace(opfStack.Peek().(efp.Token).TValue),
[]reflect.Value{reflect.ValueOf(argsList)}) []reflect.Value{reflect.ValueOf(argsStack.Peek().(*list.List))})
if arg.Type == ArgError { if arg.Type == ArgError {
return efp.Token{}, errors.New(arg.Value()) return efp.Token{}, errors.New(arg.Value())
} }
argsList.Init() argsStack.Pop()
opfStack.Pop() opfStack.Pop()
if opfStack.Len() > 0 { // still in function stack if opfStack.Len() > 0 { // still in function stack
if nextToken.TType == efp.TokenTypeOperatorInfix { if nextToken.TType == efp.TokenTypeOperatorInfix {
// mathematics calculate in formula function // mathematics calculate in formula function
opfdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) opfdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
} else { } else {
argsList.PushBack(arg) argsStack.Peek().(*list.List).PushBack(arg)
} }
} else { } else {
opdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber}) opdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
@ -1220,15 +1220,15 @@ func (fn *formulaFuncs) ATAN2(argsList *list.List) formulaArg {
if argsList.Len() != 2 { if argsList.Len() != 2 {
return newErrorFormulaArg(formulaErrorVALUE, "ATAN2 requires 2 numeric arguments") return newErrorFormulaArg(formulaErrorVALUE, "ATAN2 requires 2 numeric arguments")
} }
x, err := strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) x := argsList.Back().Value.(formulaArg).ToNumber()
if err != nil { if x.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) return x
} }
y, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) y := argsList.Front().Value.(formulaArg).ToNumber()
if err != nil { if y.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) return y
} }
return newNumberFormulaArg(math.Atan2(x, y)) return newNumberFormulaArg(math.Atan2(x.Number, y.Number))
} }
// BASE function converts a number into a supplied base (radix), and returns a // BASE function converts a number into a supplied base (radix), and returns a
@ -1243,16 +1243,17 @@ func (fn *formulaFuncs) BASE(argsList *list.List) formulaArg {
if argsList.Len() > 3 { if argsList.Len() > 3 {
return newErrorFormulaArg(formulaErrorVALUE, "BASE allows at most 3 arguments") return newErrorFormulaArg(formulaErrorVALUE, "BASE allows at most 3 arguments")
} }
var number float64 var minLength int
var radix, minLength int
var err error var err error
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { number := argsList.Front().Value.(formulaArg).ToNumber()
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) if number.Type == ArgError {
return number
} }
if radix, err = strconv.Atoi(argsList.Front().Next().Value.(formulaArg).String); err != nil { radix := argsList.Front().Next().Value.(formulaArg).ToNumber()
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) if radix.Type == ArgError {
return radix
} }
if radix < 2 || radix > 36 { if int(radix.Number) < 2 || int(radix.Number) > 36 {
return newErrorFormulaArg(formulaErrorVALUE, "radix must be an integer >= 2 and <= 36") return newErrorFormulaArg(formulaErrorVALUE, "radix must be an integer >= 2 and <= 36")
} }
if argsList.Len() > 2 { if argsList.Len() > 2 {
@ -1260,7 +1261,7 @@ func (fn *formulaFuncs) BASE(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) return newErrorFormulaArg(formulaErrorVALUE, err.Error())
} }
} }
result := strconv.FormatInt(int64(number), radix) result := strconv.FormatInt(int64(number.Number), int(radix.Number))
if len(result) < minLength { if len(result) < minLength {
result = strings.Repeat("0", minLength-len(result)) + result result = strings.Repeat("0", minLength-len(result)) + result
} }
@ -1280,18 +1281,20 @@ func (fn *formulaFuncs) CEILING(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorVALUE, "CEILING allows at most 2 arguments") return newErrorFormulaArg(formulaErrorVALUE, "CEILING allows at most 2 arguments")
} }
number, significance, res := 0.0, 1.0, 0.0 number, significance, res := 0.0, 1.0, 0.0
var err error n := argsList.Front().Value.(formulaArg).ToNumber()
number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) if n.Type == ArgError {
if err != nil { return n
return newErrorFormulaArg(formulaErrorVALUE, err.Error())
} }
number = n.Number
if number < 0 { if number < 0 {
significance = -1 significance = -1
} }
if argsList.Len() > 1 { if argsList.Len() > 1 {
if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { s := argsList.Back().Value.(formulaArg).ToNumber()
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) if s.Type == ArgError {
return s
} }
significance = s.Number
} }
if significance < 0 && number > 0 { if significance < 0 && number > 0 {
return newErrorFormulaArg(formulaErrorVALUE, "negative sig to CEILING invalid") return newErrorFormulaArg(formulaErrorVALUE, "negative sig to CEILING invalid")
@ -1319,25 +1322,30 @@ func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorVALUE, "CEILING.MATH allows at most 3 arguments") return newErrorFormulaArg(formulaErrorVALUE, "CEILING.MATH allows at most 3 arguments")
} }
number, significance, mode := 0.0, 1.0, 1.0 number, significance, mode := 0.0, 1.0, 1.0
var err error n := argsList.Front().Value.(formulaArg).ToNumber()
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { if n.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) return n
} }
number = n.Number
if number < 0 { if number < 0 {
significance = -1 significance = -1
} }
if argsList.Len() > 1 { if argsList.Len() > 1 {
if significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(formulaArg).String, 64); err != nil { s := argsList.Front().Next().Value.(formulaArg).ToNumber()
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) if s.Type == ArgError {
return s
} }
significance = s.Number
} }
if argsList.Len() == 1 { if argsList.Len() == 1 {
return newNumberFormulaArg(math.Ceil(number)) return newNumberFormulaArg(math.Ceil(number))
} }
if argsList.Len() > 2 { if argsList.Len() > 2 {
if mode, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { m := argsList.Back().Value.(formulaArg).ToNumber()
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) if m.Type == ArgError {
return m
} }
mode = m.Number
} }
val, res := math.Modf(number / significance) val, res := math.Modf(number / significance)
if res != 0 { if res != 0 {
@ -1364,11 +1372,11 @@ func (fn *formulaFuncs) CEILINGPRECISE(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorVALUE, "CEILING.PRECISE allows at most 2 arguments") return newErrorFormulaArg(formulaErrorVALUE, "CEILING.PRECISE allows at most 2 arguments")
} }
number, significance := 0.0, 1.0 number, significance := 0.0, 1.0
var err error n := argsList.Front().Value.(formulaArg).ToNumber()
number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) if n.Type == ArgError {
if err != nil { return n
return newErrorFormulaArg(formulaErrorVALUE, err.Error())
} }
number = n.Number
if number < 0 { if number < 0 {
significance = -1 significance = -1
} }
@ -1376,13 +1384,14 @@ func (fn *formulaFuncs) CEILINGPRECISE(argsList *list.List) formulaArg {
return newNumberFormulaArg(math.Ceil(number)) return newNumberFormulaArg(math.Ceil(number))
} }
if argsList.Len() > 1 { if argsList.Len() > 1 {
if significance, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { s := argsList.Back().Value.(formulaArg).ToNumber()
err = errors.New(formulaErrorVALUE) if s.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) return s
} }
significance = s.Number
significance = math.Abs(significance) significance = math.Abs(significance)
if significance == 0 { if significance == 0 {
return newStringFormulaArg("0") return newNumberFormulaArg(significance)
} }
} }
val, res := math.Modf(number / significance) val, res := math.Modf(number / significance)
@ -1404,19 +1413,22 @@ func (fn *formulaFuncs) COMBIN(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorVALUE, "COMBIN requires 2 argument") return newErrorFormulaArg(formulaErrorVALUE, "COMBIN requires 2 argument")
} }
number, chosen, val := 0.0, 0.0, 1.0 number, chosen, val := 0.0, 0.0, 1.0
var err error n := argsList.Front().Value.(formulaArg).ToNumber()
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64); err != nil { if n.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) return n
} }
if chosen, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64); err != nil { number = n.Number
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) c := argsList.Back().Value.(formulaArg).ToNumber()
if c.Type == ArgError {
return c
} }
chosen = c.Number
number, chosen = math.Trunc(number), math.Trunc(chosen) number, chosen = math.Trunc(number), math.Trunc(chosen)
if chosen > number { if chosen > number {
return newErrorFormulaArg(formulaErrorVALUE, "COMBIN requires number >= number_chosen") return newErrorFormulaArg(formulaErrorVALUE, "COMBIN requires number >= number_chosen")
} }
if chosen == number || chosen == 0 { if chosen == number || chosen == 0 {
return newStringFormulaArg("1") return newNumberFormulaArg(1)
} }
for c := float64(1); c <= chosen; c++ { for c := float64(1); c <= chosen; c++ {
val *= (number + 1 - c) / c val *= (number + 1 - c) / c
@ -1434,21 +1446,22 @@ func (fn *formulaFuncs) COMBINA(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorVALUE, "COMBINA requires 2 argument") return newErrorFormulaArg(formulaErrorVALUE, "COMBINA requires 2 argument")
} }
var number, chosen float64 var number, chosen float64
var err error n := argsList.Front().Value.(formulaArg).ToNumber()
number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) if n.Type == ArgError {
if err != nil { return n
return newErrorFormulaArg(formulaErrorVALUE, err.Error())
} }
chosen, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).String, 64) number = n.Number
if err != nil { c := argsList.Back().Value.(formulaArg).ToNumber()
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) if c.Type == ArgError {
return c
} }
chosen = c.Number
number, chosen = math.Trunc(number), math.Trunc(chosen) number, chosen = math.Trunc(number), math.Trunc(chosen)
if number < chosen { if number < chosen {
return newErrorFormulaArg(formulaErrorVALUE, "COMBINA requires number > number_chosen") return newErrorFormulaArg(formulaErrorVALUE, "COMBINA requires number > number_chosen")
} }
if number == 0 { if number == 0 {
return newStringFormulaArg("0") return newNumberFormulaArg(number)
} }
args := list.New() args := list.New()
args.PushBack(formulaArg{ args.PushBack(formulaArg{
@ -1471,11 +1484,11 @@ func (fn *formulaFuncs) COS(argsList *list.List) formulaArg {
if argsList.Len() != 1 { if argsList.Len() != 1 {
return newErrorFormulaArg(formulaErrorVALUE, "COS requires 1 numeric argument") return newErrorFormulaArg(formulaErrorVALUE, "COS requires 1 numeric argument")
} }
val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) val := argsList.Front().Value.(formulaArg).ToNumber()
if err != nil { if val.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) return val
} }
return newNumberFormulaArg(math.Cos(val)) return newNumberFormulaArg(math.Cos(val.Number))
} }
// COSH function calculates the hyperbolic cosine (cosh) of a supplied number. // COSH function calculates the hyperbolic cosine (cosh) of a supplied number.
@ -1487,11 +1500,11 @@ func (fn *formulaFuncs) COSH(argsList *list.List) formulaArg {
if argsList.Len() != 1 { if argsList.Len() != 1 {
return newErrorFormulaArg(formulaErrorVALUE, "COSH requires 1 numeric argument") return newErrorFormulaArg(formulaErrorVALUE, "COSH requires 1 numeric argument")
} }
val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) val := argsList.Front().Value.(formulaArg).ToNumber()
if err != nil { if val.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) return val
} }
return newNumberFormulaArg(math.Cosh(val)) return newNumberFormulaArg(math.Cosh(val.Number))
} }
// COT function calculates the cotangent of a given angle. The syntax of the // COT function calculates the cotangent of a given angle. The syntax of the
@ -1503,14 +1516,14 @@ func (fn *formulaFuncs) COT(argsList *list.List) formulaArg {
if argsList.Len() != 1 { if argsList.Len() != 1 {
return newErrorFormulaArg(formulaErrorVALUE, "COT requires 1 numeric argument") return newErrorFormulaArg(formulaErrorVALUE, "COT requires 1 numeric argument")
} }
val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) val := argsList.Front().Value.(formulaArg).ToNumber()
if err != nil { if val.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) return val
} }
if val == 0 { if val.Number == 0 {
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
} }
return newNumberFormulaArg(math.Tan(val)) return newNumberFormulaArg(1 / math.Tan(val.Number))
} }
// COTH function calculates the hyperbolic cotangent (coth) of a supplied // COTH function calculates the hyperbolic cotangent (coth) of a supplied
@ -1522,14 +1535,14 @@ func (fn *formulaFuncs) COTH(argsList *list.List) formulaArg {
if argsList.Len() != 1 { if argsList.Len() != 1 {
return newErrorFormulaArg(formulaErrorVALUE, "COTH requires 1 numeric argument") return newErrorFormulaArg(formulaErrorVALUE, "COTH requires 1 numeric argument")
} }
val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) val := argsList.Front().Value.(formulaArg).ToNumber()
if err != nil { if val.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) return val
} }
if val == 0 { if val.Number == 0 {
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
} }
return newNumberFormulaArg(math.Tanh(val)) return newNumberFormulaArg((math.Exp(val.Number) + math.Exp(-val.Number)) / (math.Exp(val.Number) - math.Exp(-val.Number)))
} }
// CSC function calculates the cosecant of a given angle. The syntax of the // CSC function calculates the cosecant of a given angle. The syntax of the
@ -1541,14 +1554,14 @@ func (fn *formulaFuncs) CSC(argsList *list.List) formulaArg {
if argsList.Len() != 1 { if argsList.Len() != 1 {
return newErrorFormulaArg(formulaErrorVALUE, "CSC requires 1 numeric argument") return newErrorFormulaArg(formulaErrorVALUE, "CSC requires 1 numeric argument")
} }
val, err := strconv.ParseFloat(argsList.Front().Value.(formulaArg).String, 64) val := argsList.Front().Value.(formulaArg).ToNumber()
if err != nil { if val.Type == ArgError {
return newErrorFormulaArg(formulaErrorVALUE, err.Error()) return val
} }
if val == 0 { if val.Number == 0 {
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV) return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
} }
return newNumberFormulaArg(1 / math.Sin(val)) return newNumberFormulaArg(1 / math.Sin(val.Number))
} }
// CSCH function calculates the hyperbolic cosecant (csch) of a supplied // CSCH function calculates the hyperbolic cosecant (csch) of a supplied

View File

@ -97,72 +97,84 @@ func TestCalcCellValue(t *testing.T) {
"=ATANH(0.5)": "0.549306144334055", "=ATANH(0.5)": "0.549306144334055",
"=ATANH(ATANH(0))": "0", "=ATANH(ATANH(0))": "0",
// ATAN2 // ATAN2
"=ATAN2(1,1)": "0.785398163397448", "=ATAN2(1,1)": "0.785398163397448",
"=ATAN2(1,-1)": "-0.785398163397448", "=ATAN2(1,-1)": "-0.785398163397448",
"=ATAN2(4,0)": "0", "=ATAN2(4,0)": "0",
"=ATAN2(4,ATAN2(4,0))": "0",
// BASE // BASE
"=BASE(12,2)": "1100", "=BASE(12,2)": "1100",
"=BASE(12,2,8)": "00001100", "=BASE(12,2,8)": "00001100",
"=BASE(100000,16)": "186A0", "=BASE(100000,16)": "186A0",
"=BASE(BASE(12,2),16)": "44C",
// CEILING // CEILING
"=CEILING(22.25,0.1)": "22.3", "=CEILING(22.25,0.1)": "22.3",
"=CEILING(22.25,0.5)": "22.5", "=CEILING(22.25,0.5)": "22.5",
"=CEILING(22.25,1)": "23", "=CEILING(22.25,1)": "23",
"=CEILING(22.25,10)": "30", "=CEILING(22.25,10)": "30",
"=CEILING(22.25,20)": "40", "=CEILING(22.25,20)": "40",
"=CEILING(-22.25,-0.1)": "-22.3", "=CEILING(-22.25,-0.1)": "-22.3",
"=CEILING(-22.25,-1)": "-23", "=CEILING(-22.25,-1)": "-23",
"=CEILING(-22.25,-5)": "-25", "=CEILING(-22.25,-5)": "-25",
"=CEILING(22.25)": "23", "=CEILING(22.25)": "23",
"=CEILING(CEILING(22.25,0.1),0.1)": "22.3",
// _xlfn.CEILING.MATH // _xlfn.CEILING.MATH
"=_xlfn.CEILING.MATH(15.25,1)": "16", "=_xlfn.CEILING.MATH(15.25,1)": "16",
"=_xlfn.CEILING.MATH(15.25,0.1)": "15.3", "=_xlfn.CEILING.MATH(15.25,0.1)": "15.3",
"=_xlfn.CEILING.MATH(15.25,5)": "20", "=_xlfn.CEILING.MATH(15.25,5)": "20",
"=_xlfn.CEILING.MATH(-15.25,1)": "-15", "=_xlfn.CEILING.MATH(-15.25,1)": "-15",
"=_xlfn.CEILING.MATH(-15.25,1,1)": "-15", // should be 16 "=_xlfn.CEILING.MATH(-15.25,1,1)": "-15", // should be 16
"=_xlfn.CEILING.MATH(-15.25,10)": "-10", "=_xlfn.CEILING.MATH(-15.25,10)": "-10",
"=_xlfn.CEILING.MATH(-15.25)": "-15", "=_xlfn.CEILING.MATH(-15.25)": "-15",
"=_xlfn.CEILING.MATH(-15.25,-5,-1)": "-10", "=_xlfn.CEILING.MATH(-15.25,-5,-1)": "-10",
"=_xlfn.CEILING.MATH(_xlfn.CEILING.MATH(15.25,1),1)": "16",
// _xlfn.CEILING.PRECISE // _xlfn.CEILING.PRECISE
"=_xlfn.CEILING.PRECISE(22.25,0.1)": "22.3", "=_xlfn.CEILING.PRECISE(22.25,0.1)": "22.3",
"=_xlfn.CEILING.PRECISE(22.25,0.5)": "22.5", "=_xlfn.CEILING.PRECISE(22.25,0.5)": "22.5",
"=_xlfn.CEILING.PRECISE(22.25,1)": "23", "=_xlfn.CEILING.PRECISE(22.25,1)": "23",
"=_xlfn.CEILING.PRECISE(22.25)": "23", "=_xlfn.CEILING.PRECISE(22.25)": "23",
"=_xlfn.CEILING.PRECISE(22.25,10)": "30", "=_xlfn.CEILING.PRECISE(22.25,10)": "30",
"=_xlfn.CEILING.PRECISE(22.25,0)": "0", "=_xlfn.CEILING.PRECISE(22.25,0)": "0",
"=_xlfn.CEILING.PRECISE(-22.25,1)": "-22", "=_xlfn.CEILING.PRECISE(-22.25,1)": "-22",
"=_xlfn.CEILING.PRECISE(-22.25,-1)": "-22", "=_xlfn.CEILING.PRECISE(-22.25,-1)": "-22",
"=_xlfn.CEILING.PRECISE(-22.25,5)": "-20", "=_xlfn.CEILING.PRECISE(-22.25,5)": "-20",
"=_xlfn.CEILING.PRECISE(_xlfn.CEILING.PRECISE(22.25,0.1),5)": "25",
// COMBIN // COMBIN
"=COMBIN(6,1)": "6", "=COMBIN(6,1)": "6",
"=COMBIN(6,2)": "15", "=COMBIN(6,2)": "15",
"=COMBIN(6,3)": "20", "=COMBIN(6,3)": "20",
"=COMBIN(6,4)": "15", "=COMBIN(6,4)": "15",
"=COMBIN(6,5)": "6", "=COMBIN(6,5)": "6",
"=COMBIN(6,6)": "1", "=COMBIN(6,6)": "1",
"=COMBIN(0,0)": "1", "=COMBIN(0,0)": "1",
"=COMBIN(6,COMBIN(0,0))": "6",
// _xlfn.COMBINA // _xlfn.COMBINA
"=_xlfn.COMBINA(6,1)": "6", "=_xlfn.COMBINA(6,1)": "6",
"=_xlfn.COMBINA(6,2)": "21", "=_xlfn.COMBINA(6,2)": "21",
"=_xlfn.COMBINA(6,3)": "56", "=_xlfn.COMBINA(6,3)": "56",
"=_xlfn.COMBINA(6,4)": "126", "=_xlfn.COMBINA(6,4)": "126",
"=_xlfn.COMBINA(6,5)": "252", "=_xlfn.COMBINA(6,5)": "252",
"=_xlfn.COMBINA(6,6)": "462", "=_xlfn.COMBINA(6,6)": "462",
"=_xlfn.COMBINA(0,0)": "0", "=_xlfn.COMBINA(0,0)": "0",
"=_xlfn.COMBINA(0,_xlfn.COMBINA(0,0))": "0",
// COS // COS
"=COS(0.785398163)": "0.707106781467586", "=COS(0.785398163)": "0.707106781467586",
"=COS(0)": "1", "=COS(0)": "1",
"=COS(COS(0))": "0.54030230586814",
// COSH // COSH
"=COSH(0)": "1", "=COSH(0)": "1",
"=COSH(0.5)": "1.127625965206381", "=COSH(0.5)": "1.127625965206381",
"=COSH(-2)": "3.762195691083632", "=COSH(-2)": "3.762195691083632",
"=COSH(COSH(0))": "1.543080634815244",
// _xlfn.COT // _xlfn.COT
"=_xlfn.COT(0.785398163397448)": "0.999999999999999", "=_xlfn.COT(0.785398163397448)": "1.000000000000001",
"=_xlfn.COT(_xlfn.COT(0.45))": "-0.545473116787229",
// _xlfn.COTH // _xlfn.COTH
"=_xlfn.COTH(-3.14159265358979)": "-0.99627207622075", "=_xlfn.COTH(-3.14159265358979)": "-1.003741873197322",
"=_xlfn.COTH(_xlfn.COTH(1))": "1.156014018113954",
// _xlfn.CSC // _xlfn.CSC
"=_xlfn.CSC(-6)": "3.578899547254406", "=_xlfn.CSC(-6)": "3.578899547254406",
"=_xlfn.CSC(1.5707963267949)": "1", "=_xlfn.CSC(1.5707963267949)": "1",
"=_xlfn.CSC(_xlfn.CSC(1))": "1.077851840310882",
// _xlfn.CSCH // _xlfn.CSCH
"=_xlfn.CSCH(-3.14159265358979)": "-0.086589537530047", "=_xlfn.CSCH(-3.14159265358979)": "-0.086589537530047",
// _xlfn.DECIMAL // _xlfn.DECIMAL
@ -558,7 +570,7 @@ func TestCalcCellValue(t *testing.T) {
"=BASE(1,2,3,4)": "BASE allows at most 3 arguments", "=BASE(1,2,3,4)": "BASE allows at most 3 arguments",
"=BASE(1,1)": "radix must be an integer >= 2 and <= 36", "=BASE(1,1)": "radix must be an integer >= 2 and <= 36",
`=BASE("X",2)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", `=BASE("X",2)`: "strconv.ParseFloat: parsing \"X\": invalid syntax",
`=BASE(1,"X")`: "strconv.Atoi: parsing \"X\": invalid syntax", `=BASE(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax",
`=BASE(1,2,"X")`: "strconv.Atoi: parsing \"X\": invalid syntax", `=BASE(1,2,"X")`: "strconv.Atoi: parsing \"X\": invalid syntax",
// CEILING // CEILING
"=CEILING()": "CEILING requires at least 1 argument", "=CEILING()": "CEILING requires at least 1 argument",
@ -576,7 +588,7 @@ func TestCalcCellValue(t *testing.T) {
"=_xlfn.CEILING.PRECISE()": "CEILING.PRECISE requires at least 1 argument", "=_xlfn.CEILING.PRECISE()": "CEILING.PRECISE requires at least 1 argument",
"=_xlfn.CEILING.PRECISE(1,2,3)": "CEILING.PRECISE allows at most 2 arguments", "=_xlfn.CEILING.PRECISE(1,2,3)": "CEILING.PRECISE allows at most 2 arguments",
`=_xlfn.CEILING.PRECISE("X",2)`: "strconv.ParseFloat: parsing \"X\": invalid syntax", `=_xlfn.CEILING.PRECISE("X",2)`: "strconv.ParseFloat: parsing \"X\": invalid syntax",
`=_xlfn.CEILING.PRECISE(1,"X")`: "#VALUE!", `=_xlfn.CEILING.PRECISE(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax",
// COMBIN // COMBIN
"=COMBIN()": "COMBIN requires 2 argument", "=COMBIN()": "COMBIN requires 2 argument",
"=COMBIN(-1,1)": "COMBIN requires number >= number_chosen", "=COMBIN(-1,1)": "COMBIN requires number >= number_chosen",
@ -1009,7 +1021,7 @@ func TestAND(t *testing.T) {
}) })
fn := formulaFuncs{} fn := formulaFuncs{}
result := fn.AND(argsList) result := fn.AND(argsList)
assert.Equal(t, result.String, "TRUE") assert.Equal(t, result.String, "")
assert.Empty(t, result.Error) assert.Empty(t, result.Error)
} }