ref #65, new formula functions: F.INV and GAUSS

This commit is contained in:
xuri 2022-03-10 00:05:20 +08:00
parent 74b1a998d6
commit aee7bdd3a0
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
2 changed files with 72 additions and 5 deletions

59
calc.go
View File

@ -417,6 +417,7 @@ type formulaFuncs struct {
// FALSE // FALSE
// FIND // FIND
// FINDB // FINDB
// F.INV
// F.INV.RT // F.INV.RT
// FINV // FINV
// FISHER // FISHER
@ -430,6 +431,7 @@ type formulaFuncs struct {
// FVSCHEDULE // FVSCHEDULE
// GAMMA // GAMMA
// GAMMALN // GAMMALN
// GAUSS
// GCD // GCD
// GEOMEAN // GEOMEAN
// GESTEP // GESTEP
@ -6064,6 +6066,28 @@ func (fn *formulaFuncs) GAMMALN(argsList *list.List) formulaArg {
return newErrorFormulaArg(formulaErrorVALUE, "GAMMALN requires 1 numeric argument") return newErrorFormulaArg(formulaErrorVALUE, "GAMMALN requires 1 numeric argument")
} }
// GAUSS function returns the probability that a member of a standard normal
// population will fall between the mean and a specified number of standard
// deviations from the mean. The syntax of the function is:
//
// GAUSS(z)
//
func (fn *formulaFuncs) GAUSS(argsList *list.List) formulaArg {
if argsList.Len() != 1 {
return newErrorFormulaArg(formulaErrorVALUE, "GAUSS requires 1 numeric argument")
}
args := list.New().Init()
args.PushBack(argsList.Front().Value.(formulaArg))
args.PushBack(formulaArg{Type: ArgNumber, Number: 0})
args.PushBack(formulaArg{Type: ArgNumber, Number: 1})
args.PushBack(newBoolFormulaArg(true))
normdist := fn.NORMDIST(args)
if normdist.Type != ArgNumber {
return normdist
}
return newNumberFormulaArg(normdist.Number - 0.5)
}
// GEOMEAN function calculates the geometric mean of a supplied set of values. // GEOMEAN function calculates the geometric mean of a supplied set of values.
// The syntax of the function is: // The syntax of the function is:
// //
@ -6208,8 +6232,9 @@ func (fn *formulaFuncs) EXPONDIST(argsList *list.List) formulaArg {
return newNumberFormulaArg(lambda.Number * math.Exp(-lambda.Number*x.Number)) return newNumberFormulaArg(lambda.Number * math.Exp(-lambda.Number*x.Number))
} }
// finv is an implementation of the formula functions F.INV.RT and FINV. // prepareFinvArgs checking and prepare arguments for the formula function
func (fn *formulaFuncs) finv(name string, argsList *list.List) formulaArg { // F.INV, F.INV.RT and FINV.
func (fn *formulaFuncs) prepareFinvArgs(name string, argsList *list.List) formulaArg {
if argsList.Len() != 3 { if argsList.Len() != 3 {
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 arguments", name)) return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 arguments", name))
} }
@ -6232,7 +6257,21 @@ func (fn *formulaFuncs) finv(name string, argsList *list.List) formulaArg {
if d2.Number < 1 || d2.Number >= math.Pow10(10) { if d2.Number < 1 || d2.Number >= math.Pow10(10) {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
} }
return newNumberFormulaArg((1/calcBetainv(1.0-(1.0-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1.0) * (d2.Number / d1.Number)) return newListFormulaArg([]formulaArg{probability, d1, d2})
}
// FdotINV function calculates the inverse of the Cumulative F Distribution
// for a supplied probability. The syntax of the F.Inv function is:
//
// F.INV(probability,deg_freedom1,deg_freedom2)
//
func (fn *formulaFuncs) FdotINV(argsList *list.List) formulaArg {
args := fn.prepareFinvArgs("F.INV", argsList)
if args.Type != ArgList {
return args
}
probability, d1, d2 := args.List[0], args.List[1], args.List[2]
return newNumberFormulaArg((1/calcBetainv(1-probability.Number, d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number))
} }
// FdotINVdotRT function calculates the inverse of the (right-tailed) F // FdotINVdotRT function calculates the inverse of the (right-tailed) F
@ -6242,7 +6281,12 @@ func (fn *formulaFuncs) finv(name string, argsList *list.List) formulaArg {
// F.INV.RT(probability,deg_freedom1,deg_freedom2) // F.INV.RT(probability,deg_freedom1,deg_freedom2)
// //
func (fn *formulaFuncs) FdotINVdotRT(argsList *list.List) formulaArg { func (fn *formulaFuncs) FdotINVdotRT(argsList *list.List) formulaArg {
return fn.finv("F.INV.RT", argsList) args := fn.prepareFinvArgs("F.INV.RT", argsList)
if args.Type != ArgList {
return args
}
probability, d1, d2 := args.List[0], args.List[1], args.List[2]
return newNumberFormulaArg((1/calcBetainv(1-(1-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number))
} }
// FINV function calculates the inverse of the (right-tailed) F Probability // FINV function calculates the inverse of the (right-tailed) F Probability
@ -6251,7 +6295,12 @@ func (fn *formulaFuncs) FdotINVdotRT(argsList *list.List) formulaArg {
// FINV(probability,deg_freedom1,deg_freedom2) // FINV(probability,deg_freedom1,deg_freedom2)
// //
func (fn *formulaFuncs) FINV(argsList *list.List) formulaArg { func (fn *formulaFuncs) FINV(argsList *list.List) formulaArg {
return fn.finv("FINV", argsList) args := fn.prepareFinvArgs("FINV", argsList)
if args.Type != ArgList {
return args
}
probability, d1, d2 := args.List[0], args.List[1], args.List[2]
return newNumberFormulaArg((1/calcBetainv(1-(1-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number))
} }
// NORMdotDIST function calculates the Normal Probability Density Function or // NORMdotDIST function calculates the Normal Probability Density Function or

View File

@ -838,6 +838,11 @@ func TestCalcCellValue(t *testing.T) {
// GAMMALN // GAMMALN
"=GAMMALN(4.5)": "2.45373657084244", "=GAMMALN(4.5)": "2.45373657084244",
"=GAMMALN(INT(1))": "0", "=GAMMALN(INT(1))": "0",
// GAUSS
"=GAUSS(-5)": "-0.499999713348428",
"=GAUSS(0)": "0",
"=GAUSS(0.1)": "0.039827837277029",
"=GAUSS(2.5)": "0.493790334674224",
// GEOMEAN // GEOMEAN
"=GEOMEAN(2.5,3,0.5,1,3)": "1.6226711115996", "=GEOMEAN(2.5,3,0.5,1,3)": "1.6226711115996",
// HARMEAN // HARMEAN
@ -855,6 +860,8 @@ func TestCalcCellValue(t *testing.T) {
"=EXPONDIST(0.5,1,TRUE)": "0.393469340287367", "=EXPONDIST(0.5,1,TRUE)": "0.393469340287367",
"=EXPONDIST(0.5,1,FALSE)": "0.606530659712633", "=EXPONDIST(0.5,1,FALSE)": "0.606530659712633",
"=EXPONDIST(2,1,TRUE)": "0.864664716763387", "=EXPONDIST(2,1,TRUE)": "0.864664716763387",
// F.INV
"=F.INV(0.9,2,5)": "3.77971607877395",
// FINV // FINV
"=FINV(0.2,1,2)": "3.55555555555555", "=FINV(0.2,1,2)": "3.55555555555555",
"=FINV(0.6,1,2)": "0.380952380952381", "=FINV(0.6,1,2)": "0.380952380952381",
@ -2380,6 +2387,9 @@ func TestCalcCellValue(t *testing.T) {
"=GAMMALN(F1)": "GAMMALN requires 1 numeric argument", "=GAMMALN(F1)": "GAMMALN requires 1 numeric argument",
"=GAMMALN(0)": "#N/A", "=GAMMALN(0)": "#N/A",
"=GAMMALN(INT(0))": "#N/A", "=GAMMALN(INT(0))": "#N/A",
// GAUSS
"=GAUSS()": "GAUSS requires 1 numeric argument",
"=GAUSS(\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
// GEOMEAN // GEOMEAN
"=GEOMEAN()": "GEOMEAN requires at least 1 numeric argument", "=GEOMEAN()": "GEOMEAN requires at least 1 numeric argument",
"=GEOMEAN(0)": "#NUM!", "=GEOMEAN(0)": "#NUM!",
@ -2405,6 +2415,14 @@ func TestCalcCellValue(t *testing.T) {
"=EXPONDIST(0,1,\"\")": "strconv.ParseBool: parsing \"\": invalid syntax", "=EXPONDIST(0,1,\"\")": "strconv.ParseBool: parsing \"\": invalid syntax",
"=EXPONDIST(-1,1,TRUE)": "#NUM!", "=EXPONDIST(-1,1,TRUE)": "#NUM!",
"=EXPONDIST(1,0,TRUE)": "#NUM!", "=EXPONDIST(1,0,TRUE)": "#NUM!",
// F.INV
"=F.INV()": "F.INV requires 3 arguments",
"=F.INV(\"\",1,2)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=F.INV(0.2,\"\",2)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=F.INV(0.2,1,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=F.INV(0,1,2)": "#NUM!",
"=F.INV(0.2,0.5,2)": "#NUM!",
"=F.INV(0.2,1,0.5)": "#NUM!",
// FINV // FINV
"=FINV()": "FINV requires 3 arguments", "=FINV()": "FINV requires 3 arguments",
"=FINV(\"\",1,2)": "strconv.ParseFloat: parsing \"\": invalid syntax", "=FINV(\"\",1,2)": "strconv.ParseFloat: parsing \"\": invalid syntax",