* New formula functions: BETA.DIST, BINOMDIST and BINOM * Fix a part of formula function calculation result precision issue on arm64
This commit is contained in:
parent
94f197c4fe
commit
49424b0eb3
142
calc.go
142
calc.go
|
@ -334,11 +334,14 @@ type formulaFuncs struct {
|
||||||
// BESSELK
|
// BESSELK
|
||||||
// BESSELY
|
// BESSELY
|
||||||
// BETADIST
|
// BETADIST
|
||||||
|
// BETA.DIST
|
||||||
// BETAINV
|
// BETAINV
|
||||||
// BETA.INV
|
// BETA.INV
|
||||||
// BIN2DEC
|
// BIN2DEC
|
||||||
// BIN2HEX
|
// BIN2HEX
|
||||||
// BIN2OCT
|
// BIN2OCT
|
||||||
|
// BINOMDIST
|
||||||
|
// BINOM.DIST
|
||||||
// BITAND
|
// BITAND
|
||||||
// BITLSHIFT
|
// BITLSHIFT
|
||||||
// BITOR
|
// BITOR
|
||||||
|
@ -686,7 +689,7 @@ func (f *File) CalcCellValue(sheet, cell string) (result string, err error) {
|
||||||
}
|
}
|
||||||
result = token.TValue
|
result = token.TValue
|
||||||
isNum, precision := isNumeric(result)
|
isNum, precision := isNumeric(result)
|
||||||
if isNum && precision > 15 {
|
if isNum && (precision > 15 || precision == 0) {
|
||||||
num := roundPrecision(result, -1)
|
num := roundPrecision(result, -1)
|
||||||
result = strings.ToUpper(num)
|
result = strings.ToUpper(num)
|
||||||
}
|
}
|
||||||
|
@ -5406,6 +5409,74 @@ func getBetaDist(fXin, fAlpha, fBeta float64) float64 {
|
||||||
return fResult
|
return fResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prepareBETAdotDISTArgs checking and prepare arguments for the formula
|
||||||
|
// function BETA.DIST.
|
||||||
|
func (fn *formulaFuncs) prepareBETAdotDISTArgs(argsList *list.List) formulaArg {
|
||||||
|
if argsList.Len() < 4 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, "BETA.DIST requires at least 4 arguments")
|
||||||
|
}
|
||||||
|
if argsList.Len() > 6 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, "BETA.DIST requires at most 6 arguments")
|
||||||
|
}
|
||||||
|
x := argsList.Front().Value.(formulaArg).ToNumber()
|
||||||
|
if x.Type != ArgNumber {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
alpha := argsList.Front().Next().Value.(formulaArg).ToNumber()
|
||||||
|
if alpha.Type != ArgNumber {
|
||||||
|
return alpha
|
||||||
|
}
|
||||||
|
beta := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
|
||||||
|
if beta.Type != ArgNumber {
|
||||||
|
return beta
|
||||||
|
}
|
||||||
|
if alpha.Number <= 0 || beta.Number <= 0 {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
cumulative := argsList.Front().Next().Next().Next().Value.(formulaArg).ToBool()
|
||||||
|
if cumulative.Type != ArgNumber {
|
||||||
|
return cumulative
|
||||||
|
}
|
||||||
|
a, b := newNumberFormulaArg(0), newNumberFormulaArg(1)
|
||||||
|
if argsList.Len() > 4 {
|
||||||
|
if a = argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); a.Type != ArgNumber {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if argsList.Len() == 6 {
|
||||||
|
if b = argsList.Back().Value.(formulaArg).ToNumber(); b.Type != ArgNumber {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newListFormulaArg([]formulaArg{x, alpha, beta, cumulative, a, b})
|
||||||
|
}
|
||||||
|
|
||||||
|
// BETAdotDIST function calculates the cumulative beta distribution function
|
||||||
|
// or the probability density function of the Beta distribution, for a
|
||||||
|
// supplied set of parameters. The syntax of the function is:
|
||||||
|
//
|
||||||
|
// BETA.DIST(x,alpha,beta,cumulative,[A],[B])
|
||||||
|
//
|
||||||
|
func (fn *formulaFuncs) BETAdotDIST(argsList *list.List) formulaArg {
|
||||||
|
args := fn.prepareBETAdotDISTArgs(argsList)
|
||||||
|
if args.Type != ArgList {
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
x, alpha, beta, cumulative, a, b := args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5]
|
||||||
|
if x.Number < a.Number || x.Number > b.Number {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
if a.Number == b.Number {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
fScale := b.Number - a.Number
|
||||||
|
x.Number = (x.Number - a.Number) / fScale
|
||||||
|
if cumulative.Number == 1 {
|
||||||
|
return newNumberFormulaArg(getBetaDist(x.Number, alpha.Number, beta.Number))
|
||||||
|
}
|
||||||
|
return newNumberFormulaArg(getBetaDistPDF(x.Number, alpha.Number, beta.Number) / fScale)
|
||||||
|
}
|
||||||
|
|
||||||
// BETADIST function calculates the cumulative beta probability density
|
// BETADIST function calculates the cumulative beta probability density
|
||||||
// function for a supplied set of parameters. The syntax of the function is:
|
// function for a supplied set of parameters. The syntax of the function is:
|
||||||
//
|
//
|
||||||
|
@ -5836,6 +5907,69 @@ func incompleteGamma(a, x float64) float64 {
|
||||||
return math.Pow(x, a) * math.Exp(0-x) * summer
|
return math.Pow(x, a) * math.Exp(0-x) * summer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// binomCoeff implement binomial coefficient calcuation.
|
||||||
|
func binomCoeff(n, k float64) float64 {
|
||||||
|
return fact(n) / (fact(k) * fact(n-k))
|
||||||
|
}
|
||||||
|
|
||||||
|
// binomdist implement binomial distribution calcuation.
|
||||||
|
func binomdist(x, n, p float64) float64 {
|
||||||
|
return binomCoeff(n, x) * math.Pow(p, x) * math.Pow(1-p, n-x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BINOMfotDIST function returns the Binomial Distribution probability for a
|
||||||
|
// given number of successes from a specified number of trials. The syntax of
|
||||||
|
// the function is:
|
||||||
|
//
|
||||||
|
// BINOM.DIST(number_s,trials,probability_s,cumulative)
|
||||||
|
//
|
||||||
|
func (fn *formulaFuncs) BINOMdotDIST(argsList *list.List) formulaArg {
|
||||||
|
if argsList.Len() != 4 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, "BINOM.DIST requires 4 arguments")
|
||||||
|
}
|
||||||
|
return fn.BINOMDIST(argsList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BINOMDIST function returns the Binomial Distribution probability of a
|
||||||
|
// specified number of successes out of a specified number of trials. The
|
||||||
|
// syntax of the function is:
|
||||||
|
//
|
||||||
|
// BINOMDIST(number_s,trials,probability_s,cumulative)
|
||||||
|
//
|
||||||
|
func (fn *formulaFuncs) BINOMDIST(argsList *list.List) formulaArg {
|
||||||
|
if argsList.Len() != 4 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, "BINOMDIST requires 4 arguments")
|
||||||
|
}
|
||||||
|
var s, trials, probability, cumulative formulaArg
|
||||||
|
if s = argsList.Front().Value.(formulaArg).ToNumber(); s.Type != ArgNumber {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
if trials = argsList.Front().Next().Value.(formulaArg).ToNumber(); trials.Type != ArgNumber {
|
||||||
|
return trials
|
||||||
|
}
|
||||||
|
if s.Number < 0 || s.Number > trials.Number {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
if probability = argsList.Back().Prev().Value.(formulaArg).ToNumber(); probability.Type != ArgNumber {
|
||||||
|
return probability
|
||||||
|
}
|
||||||
|
|
||||||
|
if probability.Number < 0 || probability.Number > 1 {
|
||||||
|
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
|
||||||
|
}
|
||||||
|
if cumulative = argsList.Back().Value.(formulaArg).ToBool(); cumulative.Type == ArgError {
|
||||||
|
return cumulative
|
||||||
|
}
|
||||||
|
if cumulative.Number == 1 {
|
||||||
|
bm := 0.0
|
||||||
|
for i := 0; i <= int(s.Number); i++ {
|
||||||
|
bm += binomdist(float64(i), trials.Number, probability.Number)
|
||||||
|
}
|
||||||
|
return newNumberFormulaArg(bm)
|
||||||
|
}
|
||||||
|
return newNumberFormulaArg(binomdist(s.Number, trials.Number, probability.Number))
|
||||||
|
}
|
||||||
|
|
||||||
// CHIDIST function calculates the right-tailed probability of the chi-square
|
// CHIDIST function calculates the right-tailed probability of the chi-square
|
||||||
// distribution. The syntax of the function is:
|
// distribution. The syntax of the function is:
|
||||||
//
|
//
|
||||||
|
@ -11641,7 +11775,7 @@ func (fn *formulaFuncs) AMORLINC(argsList *list.List) formulaArg {
|
||||||
if int(period.Number) <= periods {
|
if int(period.Number) <= periods {
|
||||||
return newNumberFormulaArg(rate2)
|
return newNumberFormulaArg(rate2)
|
||||||
} else if int(period.Number)-1 == periods {
|
} else if int(period.Number)-1 == periods {
|
||||||
return newNumberFormulaArg(delta - rate2*float64(periods) - rate1)
|
return newNumberFormulaArg(delta - rate2*float64(periods) - math.Nextafter(rate1, rate1))
|
||||||
}
|
}
|
||||||
return newNumberFormulaArg(0)
|
return newNumberFormulaArg(0)
|
||||||
}
|
}
|
||||||
|
@ -13334,7 +13468,9 @@ func (fn *formulaFuncs) rate(nper, pmt, pv, fv, t, guess formulaArg, argsList *l
|
||||||
rt := rate*t.Number + 1
|
rt := rate*t.Number + 1
|
||||||
p0 := pmt.Number * (t1 - 1)
|
p0 := pmt.Number * (t1 - 1)
|
||||||
f1 := fv.Number + t1*pv.Number + p0*rt/rate
|
f1 := fv.Number + t1*pv.Number + p0*rt/rate
|
||||||
f2 := nper.Number*t2*pv.Number - p0*rt/math.Pow(rate, 2)
|
n1 := nper.Number * t2 * pv.Number
|
||||||
|
n2 := p0 * rt / math.Pow(rate, 2)
|
||||||
|
f2 := math.Nextafter(n1, n1) - math.Nextafter(n2, n2)
|
||||||
f3 := (nper.Number*pmt.Number*t2*rt + p0*t.Number) / rate
|
f3 := (nper.Number*pmt.Number*t2*rt + p0*t.Number) / rate
|
||||||
delta := f1 / (f2 + f3)
|
delta := f1 / (f2 + f3)
|
||||||
if math.Abs(delta) < epsMax {
|
if math.Abs(delta) < epsMax {
|
||||||
|
|
65
calc_test.go
65
calc_test.go
|
@ -784,6 +784,9 @@ func TestCalcCellValue(t *testing.T) {
|
||||||
"=AVERAGEA(A1)": "1",
|
"=AVERAGEA(A1)": "1",
|
||||||
"=AVERAGEA(A1:A2)": "1.5",
|
"=AVERAGEA(A1:A2)": "1.5",
|
||||||
"=AVERAGEA(D2:F9)": "12671.375",
|
"=AVERAGEA(D2:F9)": "12671.375",
|
||||||
|
// BETA.DIST
|
||||||
|
"=BETA.DIST(0.4,4,5,TRUE,0,1)": "0.4059136",
|
||||||
|
"=BETA.DIST(0.6,4,5,FALSE,0,1)": "1.548288",
|
||||||
// BETADIST
|
// BETADIST
|
||||||
"=BETADIST(0.4,4,5)": "0.4059136",
|
"=BETADIST(0.4,4,5)": "0.4059136",
|
||||||
"=BETADIST(0.4,4,5,0,1)": "0.4059136",
|
"=BETADIST(0.4,4,5,0,1)": "0.4059136",
|
||||||
|
@ -796,12 +799,26 @@ func TestCalcCellValue(t *testing.T) {
|
||||||
"=BETADIST(0.4,2,100)": "1",
|
"=BETADIST(0.4,2,100)": "1",
|
||||||
"=BETADIST(0.75,3,4)": "0.96240234375",
|
"=BETADIST(0.75,3,4)": "0.96240234375",
|
||||||
"=BETADIST(0.2,0.7,4)": "0.71794309318323",
|
"=BETADIST(0.2,0.7,4)": "0.71794309318323",
|
||||||
"=BETADIST(0.01,3,4)": "1.9553589999999998e-05",
|
"=BETADIST(0.01,3,4)": "1.955359E-05",
|
||||||
"=BETADIST(0.75,130,140)": "1",
|
"=BETADIST(0.75,130,140)": "1",
|
||||||
// BETAINV
|
// BETAINV
|
||||||
"=BETAINV(0.2,4,5,0,1)": "0.303225844664082",
|
"=BETAINV(0.2,4,5,0,1)": "0.303225844664082",
|
||||||
// BETA.INV
|
// BETA.INV
|
||||||
"=BETA.INV(0.2,4,5,0,1)": "0.303225844664082",
|
"=BETA.INV(0.2,4,5,0,1)": "0.303225844664082",
|
||||||
|
// BINOMDIST
|
||||||
|
"=BINOMDIST(10,100,0.5,FALSE)": "1.36554263874631E-17",
|
||||||
|
"=BINOMDIST(50,100,0.5,FALSE)": "0.0795892373871787",
|
||||||
|
"=BINOMDIST(65,100,0.5,FALSE)": "0.000863855665741652",
|
||||||
|
"=BINOMDIST(10,100,0.5,TRUE)": "1.53164508771899E-17",
|
||||||
|
"=BINOMDIST(50,100,0.5,TRUE)": "0.539794618693589",
|
||||||
|
"=BINOMDIST(65,100,0.5,TRUE)": "0.999105034804256",
|
||||||
|
// BINOM.DIST
|
||||||
|
"=BINOM.DIST(10,100,0.5,FALSE)": "1.36554263874631E-17",
|
||||||
|
"=BINOM.DIST(50,100,0.5,FALSE)": "0.0795892373871787",
|
||||||
|
"=BINOM.DIST(65,100,0.5,FALSE)": "0.000863855665741652",
|
||||||
|
"=BINOM.DIST(10,100,0.5,TRUE)": "1.53164508771899E-17",
|
||||||
|
"=BINOM.DIST(50,100,0.5,TRUE)": "0.539794618693589",
|
||||||
|
"=BINOM.DIST(65,100,0.5,TRUE)": "0.999105034804256",
|
||||||
// CHIDIST
|
// CHIDIST
|
||||||
"=CHIDIST(0.5,3)": "0.918891411654676",
|
"=CHIDIST(0.5,3)": "0.918891411654676",
|
||||||
"=CHIDIST(8,3)": "0.0460117056892315",
|
"=CHIDIST(8,3)": "0.0460117056892315",
|
||||||
|
@ -1468,7 +1485,7 @@ func TestCalcCellValue(t *testing.T) {
|
||||||
"=UPPER(\"TEST 123\")": "TEST 123",
|
"=UPPER(\"TEST 123\")": "TEST 123",
|
||||||
// VALUE
|
// VALUE
|
||||||
"=VALUE(\"50\")": "50",
|
"=VALUE(\"50\")": "50",
|
||||||
"=VALUE(\"1.0E-07\")": "1e-07",
|
"=VALUE(\"1.0E-07\")": "1E-07",
|
||||||
"=VALUE(\"5,000\")": "5000",
|
"=VALUE(\"5,000\")": "5000",
|
||||||
"=VALUE(\"20%\")": "0.2",
|
"=VALUE(\"20%\")": "0.2",
|
||||||
"=VALUE(\"12:00:00\")": "0.5",
|
"=VALUE(\"12:00:00\")": "0.5",
|
||||||
|
@ -2341,6 +2358,25 @@ func TestCalcCellValue(t *testing.T) {
|
||||||
"=AVERAGE(H1)": "AVERAGE divide by zero",
|
"=AVERAGE(H1)": "AVERAGE divide by zero",
|
||||||
// AVERAGEA
|
// AVERAGEA
|
||||||
"=AVERAGEA(H1)": "AVERAGEA divide by zero",
|
"=AVERAGEA(H1)": "AVERAGEA divide by zero",
|
||||||
|
// AVERAGEIF
|
||||||
|
"=AVERAGEIF()": "AVERAGEIF requires at least 2 arguments",
|
||||||
|
"=AVERAGEIF(H1,\"\")": "AVERAGEIF divide by zero",
|
||||||
|
"=AVERAGEIF(D1:D3,\"Month\",D1:D3)": "AVERAGEIF divide by zero",
|
||||||
|
"=AVERAGEIF(C1:C3,\"Month\",D1:D3)": "AVERAGEIF divide by zero",
|
||||||
|
// BETA.DIST
|
||||||
|
"=BETA.DIST()": "BETA.DIST requires at least 4 arguments",
|
||||||
|
"=BETA.DIST(0.4,4,5,TRUE,0,1,0)": "BETA.DIST requires at most 6 arguments",
|
||||||
|
"=BETA.DIST(\"\",4,5,TRUE,0,1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=BETA.DIST(0.4,\"\",5,TRUE,0,1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=BETA.DIST(0.4,4,\"\",TRUE,0,1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=BETA.DIST(0.4,4,5,\"\",0,1)": "strconv.ParseBool: parsing \"\": invalid syntax",
|
||||||
|
"=BETA.DIST(0.4,4,5,TRUE,\"\",1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=BETA.DIST(0.4,4,5,TRUE,0,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=BETA.DIST(0.4,0,5,TRUE,0,1)": "#NUM!",
|
||||||
|
"=BETA.DIST(0.4,4,0,TRUE,0,0)": "#NUM!",
|
||||||
|
"=BETA.DIST(0.4,4,5,TRUE,0.5,1)": "#NUM!",
|
||||||
|
"=BETA.DIST(0.4,4,5,TRUE,0,0.3)": "#NUM!",
|
||||||
|
"=BETA.DIST(0.4,4,5,TRUE,0.4,0.4)": "#NUM!",
|
||||||
// BETADIST
|
// BETADIST
|
||||||
"=BETADIST()": "BETADIST requires at least 3 arguments",
|
"=BETADIST()": "BETADIST requires at least 3 arguments",
|
||||||
"=BETADIST(0.4,4,5,0,1,0)": "BETADIST requires at most 5 arguments",
|
"=BETADIST(0.4,4,5,0,1,0)": "BETADIST requires at most 5 arguments",
|
||||||
|
@ -2380,11 +2416,26 @@ func TestCalcCellValue(t *testing.T) {
|
||||||
"=BETA.INV(0.2,0,5,0,1)": "#NUM!",
|
"=BETA.INV(0.2,0,5,0,1)": "#NUM!",
|
||||||
"=BETA.INV(0.2,4,0,0,1)": "#NUM!",
|
"=BETA.INV(0.2,4,0,0,1)": "#NUM!",
|
||||||
"=BETA.INV(0.2,4,5,2,2)": "#NUM!",
|
"=BETA.INV(0.2,4,5,2,2)": "#NUM!",
|
||||||
// AVERAGEIF
|
// BINOMDIST
|
||||||
"=AVERAGEIF()": "AVERAGEIF requires at least 2 arguments",
|
"=BINOMDIST()": "BINOMDIST requires 4 arguments",
|
||||||
"=AVERAGEIF(H1,\"\")": "AVERAGEIF divide by zero",
|
"=BINOMDIST(\"\",100,0.5,FALSE)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
"=AVERAGEIF(D1:D3,\"Month\",D1:D3)": "AVERAGEIF divide by zero",
|
"=BINOMDIST(10,\"\",0.5,FALSE)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
"=AVERAGEIF(C1:C3,\"Month\",D1:D3)": "AVERAGEIF divide by zero",
|
"=BINOMDIST(10,100,\"\",FALSE)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=BINOMDIST(10,100,0.5,\"\")": "strconv.ParseBool: parsing \"\": invalid syntax",
|
||||||
|
"=BINOMDIST(-1,100,0.5,FALSE)": "#NUM!",
|
||||||
|
"=BINOMDIST(110,100,0.5,FALSE)": "#NUM!",
|
||||||
|
"=BINOMDIST(10,100,-1,FALSE)": "#NUM!",
|
||||||
|
"=BINOMDIST(10,100,2,FALSE)": "#NUM!",
|
||||||
|
// BINOM.DIST
|
||||||
|
"=BINOM.DIST()": "BINOM.DIST requires 4 arguments",
|
||||||
|
"=BINOM.DIST(\"\",100,0.5,FALSE)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=BINOM.DIST(10,\"\",0.5,FALSE)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=BINOM.DIST(10,100,\"\",FALSE)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
"=BINOM.DIST(10,100,0.5,\"\")": "strconv.ParseBool: parsing \"\": invalid syntax",
|
||||||
|
"=BINOM.DIST(-1,100,0.5,FALSE)": "#NUM!",
|
||||||
|
"=BINOM.DIST(110,100,0.5,FALSE)": "#NUM!",
|
||||||
|
"=BINOM.DIST(10,100,-1,FALSE)": "#NUM!",
|
||||||
|
"=BINOM.DIST(10,100,2,FALSE)": "#NUM!",
|
||||||
// CHIDIST
|
// CHIDIST
|
||||||
"=CHIDIST()": "CHIDIST requires 2 numeric arguments",
|
"=CHIDIST()": "CHIDIST requires 2 numeric arguments",
|
||||||
"=CHIDIST(\"\",3)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
"=CHIDIST(\"\",3)": "strconv.ParseFloat: parsing \"\": invalid syntax",
|
||||||
|
|
Loading…
Reference in New Issue