summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxuri <xuri.me@gmail.com>2020-05-04 21:22:11 +0800
committerxuri <xuri.me@gmail.com>2020-05-04 21:22:11 +0800
commit6f796b88e68e927c71e51e22278f4d43b935e00a (patch)
tree933ed76ec76d255d665e87f797efc414ab25a447
parent789adf9202b4bc04e74ea8fe72138f1942b2cc0c (diff)
fn: CEILING, CEILING.MATH
-rw-r--r--calc.go101
-rw-r--r--calc_test.go29
2 files changed, 126 insertions, 4 deletions
diff --git a/calc.go b/calc.go
index 7c912eb..568f044 100644
--- a/calc.go
+++ b/calc.go
@@ -220,7 +220,9 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
argsList.PushBack(opfdStack.Pop())
}
// call formula function to evaluate
- result, err := callFuncByName(&formulaFuncs{}, strings.ReplaceAll(opfStack.Peek().(efp.Token).TValue, "_xlfn.", ""), []reflect.Value{reflect.ValueOf(argsList)})
+ result, err := callFuncByName(&formulaFuncs{}, strings.NewReplacer(
+ "_xlfn", "", ".", "").Replace(opfStack.Peek().(efp.Token).TValue),
+ []reflect.Value{reflect.ValueOf(argsList)})
if err != nil {
return efp.Token{}, err
}
@@ -801,6 +803,103 @@ func (fn *formulaFuncs) BASE(argsList *list.List) (result string, err error) {
return
}
+// CEILING function rounds a supplied number away from zero, to the nearest
+// multiple of a given number. The syntax of the function is:
+//
+// CEILING(number,significance)
+//
+func (fn *formulaFuncs) CEILING(argsList *list.List) (result string, err error) {
+ if argsList.Len() == 0 {
+ err = errors.New("CEILING requires at least 1 argument")
+ return
+ }
+ if argsList.Len() > 2 {
+ err = errors.New("CEILING allows at most 2 arguments")
+ return
+ }
+ var number, significance float64
+ number, err = strconv.ParseFloat(argsList.Front().Value.(efp.Token).TValue, 64)
+ if err != nil {
+ return
+ }
+ significance = 1
+ if number < 0 {
+ significance = -1
+ }
+ if argsList.Len() > 1 {
+ significance, err = strconv.ParseFloat(argsList.Back().Value.(efp.Token).TValue, 64)
+ if err != nil {
+ return
+ }
+ }
+ if significance < 0 && number > 0 {
+ err = errors.New("negative sig to CEILING invalid")
+ return
+ }
+ if argsList.Len() == 1 {
+ result = fmt.Sprintf("%g", math.Ceil(number))
+ return
+ }
+ number, res := math.Modf(number / significance)
+ if res > 0 {
+ number++
+ }
+ result = fmt.Sprintf("%g", number*significance)
+ return
+}
+
+// CEILINGMATH function rounds a supplied number up to a supplied multiple of
+// significance. The syntax of the function is:
+//
+// CEILING.MATH(number,[significance],[mode])
+//
+func (fn *formulaFuncs) CEILINGMATH(argsList *list.List) (result string, err error) {
+ if argsList.Len() == 0 {
+ err = errors.New("CEILING.MATH requires at least 1 argument")
+ return
+ }
+ if argsList.Len() > 3 {
+ err = errors.New("CEILING.MATH allows at most 3 arguments")
+ return
+ }
+ var number, significance, mode float64 = 0, 1, 1
+ number, err = strconv.ParseFloat(argsList.Front().Value.(efp.Token).TValue, 64)
+ if err != nil {
+ return
+ }
+ if number < 0 {
+ significance = -1
+ }
+ if argsList.Len() > 1 {
+ significance, err = strconv.ParseFloat(argsList.Front().Next().Value.(efp.Token).TValue, 64)
+ if err != nil {
+ return
+ }
+ }
+ if argsList.Len() == 1 {
+ result = fmt.Sprintf("%g", math.Ceil(number))
+ return
+ }
+ if argsList.Len() > 2 {
+ mode, err = strconv.ParseFloat(argsList.Back().Value.(efp.Token).TValue, 64)
+ if err != nil {
+ return
+ }
+ }
+ val, res := math.Modf(number / significance)
+ _, _ = res, mode
+ if res != 0 {
+ if number > 0 {
+ val++
+ } else if mode < 0 {
+ val--
+ }
+ }
+
+ result = fmt.Sprintf("%g", val*significance)
+ return
+}
+
// GCD function returns the greatest common divisor of two or more supplied
// integers. The syntax of the function is:
//
diff --git a/calc_test.go b/calc_test.go
index bb8ae8a..a14cc0c 100644
--- a/calc_test.go
+++ b/calc_test.go
@@ -67,6 +67,22 @@ func TestCalcCellValue(t *testing.T) {
"=BASE(12,2)": "1100",
"=BASE(12,2,8)": "00001100",
"=BASE(100000,16)": "186A0",
+ // CEILING
+ "=CEILING(22.25,0.1)": "22.3",
+ "=CEILING(22.25,0.5)": "22.5",
+ "=CEILING(22.25,1)": "23",
+ "=CEILING(22.25,10)": "30",
+ "=CEILING(22.25,20)": "40",
+ "=CEILING(-22.25,-0.1)": "-22.3",
+ "=CEILING(-22.25,-1)": "-23",
+ "=CEILING(-22.25,-5)": "-25",
+ // _xlfn.CEILING.MATH
+ "=_xlfn.CEILING.MATH(15.25,1)": "16",
+ "=_xlfn.CEILING.MATH(15.25,0.1)": "15.3",
+ "=_xlfn.CEILING.MATH(15.25,5)": "20",
+ "=_xlfn.CEILING.MATH(-15.25,1)": "-15",
+ "=_xlfn.CEILING.MATH(-15.25,1,1)": "-15", // should be 16
+ "=_xlfn.CEILING.MATH(-15.25,10)": "-10",
// GCD
"=GCD(1,5)": "1",
"=GCD(15,10,25)": "5",
@@ -123,11 +139,11 @@ func TestCalcCellValue(t *testing.T) {
"=ACOS()": "ACOS requires 1 numeric arguments",
// ACOSH
"=ACOSH()": "ACOSH requires 1 numeric arguments",
- // ACOT
+ // _xlfn.ACOT
"=_xlfn.ACOT()": "ACOT requires 1 numeric arguments",
- // ACOTH
+ // _xlfn.ACOTH
"=_xlfn.ACOTH()": "ACOTH requires 1 numeric arguments",
- // ARABIC
+ // _xlfn.ARABIC
"_xlfn.ARABIC()": "ARABIC requires 1 numeric arguments",
// ASIN
"=ASIN()": "ASIN requires 1 numeric arguments",
@@ -143,6 +159,13 @@ func TestCalcCellValue(t *testing.T) {
"=BASE()": "BASE requires at least 2 arguments",
"=BASE(1,2,3,4)": "BASE allows at most 3 arguments",
"=BASE(1,1)": "radix must be an integer ≥ 2 and ≤ 36",
+ // CEILING
+ "=CEILING()": "CEILING requires at least 1 argument",
+ "=CEILING(1,2,3)": "CEILING allows at most 2 arguments",
+ "=CEILING(1,-1)": "negative sig to CEILING invalid",
+ // _xlfn.CEILING.MATH
+ "=_xlfn.CEILING.MATH()": "CEILING.MATH requires at least 1 argument",
+ "=_xlfn.CEILING.MATH(1,2,3,4)": "CEILING.MATH allows at most 3 arguments",
// GCD
"=GCD()": "GCD requires at least 1 argument",
"=GCD(-1)": "GCD only accepts positive arguments",