Browse Source

related issue #65 fn: FIXED

xuri 4 years ago
parent
commit
874d59cee0
4 changed files with 77 additions and 13 deletions
  1. 52 0
      calc.go
  2. 15 0
      calc_test.go
  3. 3 3
      go.mod
  4. 7 10
      go.sum

+ 52 - 0
calc.go

@@ -29,6 +29,8 @@ import (
 	"unsafe"
 	"unsafe"
 
 
 	"github.com/xuri/efp"
 	"github.com/xuri/efp"
+	"golang.org/x/text/language"
+	"golang.org/x/text/message"
 )
 )
 
 
 // Excel formula errors
 // Excel formula errors
@@ -273,6 +275,7 @@ var tokenPriority = map[string]int{
 //    FINDB
 //    FINDB
 //    FISHER
 //    FISHER
 //    FISHERINV
 //    FISHERINV
+//    FIXED
 //    FLOOR
 //    FLOOR
 //    FLOOR.MATH
 //    FLOOR.MATH
 //    FLOOR.PRECISE
 //    FLOOR.PRECISE
@@ -4883,6 +4886,55 @@ func (fn *formulaFuncs) EXACT(argsList *list.List) formulaArg {
 	return newBoolFormulaArg(text1 == text2)
 	return newBoolFormulaArg(text1 == text2)
 }
 }
 
 
+// FIXED function rounds a supplied number to a specified number of decimal
+// places and then converts this into text. The syntax of the function is:
+//
+//    FIXED(number,[decimals],[no_commas])
+//
+func (fn *formulaFuncs) FIXED(argsList *list.List) formulaArg {
+	if argsList.Len() < 1 {
+		return newErrorFormulaArg(formulaErrorVALUE, "FIXED requires at least 1 argument")
+	}
+	if argsList.Len() > 3 {
+		return newErrorFormulaArg(formulaErrorVALUE, "FIXED allows at most 3 arguments")
+	}
+	numArg := argsList.Front().Value.(formulaArg).ToNumber()
+	if numArg.Type != ArgNumber {
+		return numArg
+	}
+	precision, decimals, noCommas := 0, 0, false
+	s := strings.Split(argsList.Front().Value.(formulaArg).Value(), ".")
+	if argsList.Len() == 1 && len(s) == 2 {
+		precision = len(s[1])
+		decimals = len(s[1])
+	}
+	if argsList.Len() >= 2 {
+		decimalsArg := argsList.Front().Next().Value.(formulaArg).ToNumber()
+		if decimalsArg.Type != ArgNumber {
+			return decimalsArg
+		}
+		decimals = int(decimalsArg.Number)
+	}
+	if argsList.Len() == 3 {
+		noCommasArg := argsList.Back().Value.(formulaArg).ToBool()
+		if noCommasArg.Type == ArgError {
+			return noCommasArg
+		}
+		noCommas = noCommasArg.Boolean
+	}
+	n := math.Pow(10, float64(decimals))
+	r := numArg.Number * n
+	fixed := float64(int(r+math.Copysign(0.5, r))) / n
+	if decimals > 0 {
+		precision = decimals
+	}
+	if noCommas {
+		return newStringFormulaArg(fmt.Sprintf(fmt.Sprintf("%%.%df", precision), fixed))
+	}
+	p := message.NewPrinter(language.English)
+	return newStringFormulaArg(p.Sprintf(fmt.Sprintf("%%.%df", precision), fixed))
+}
+
 // FIND function returns the position of a specified character or sub-string
 // FIND function returns the position of a specified character or sub-string
 // within a supplied text string. The function is case-sensitive. The syntax
 // within a supplied text string. The function is case-sensitive. The syntax
 // of the function is:
 // of the function is:

+ 15 - 0
calc_test.go

@@ -759,6 +759,15 @@ func TestCalcCellValue(t *testing.T) {
 		"=EXACT(1,\"1\")":     "TRUE",
 		"=EXACT(1,\"1\")":     "TRUE",
 		"=EXACT(1,1)":         "TRUE",
 		"=EXACT(1,1)":         "TRUE",
 		"=EXACT(\"A\",\"a\")": "FALSE",
 		"=EXACT(\"A\",\"a\")": "FALSE",
+		// FIXED
+		"=FIXED(5123.591)":         "5,123.591",
+		"=FIXED(5123.591,1)":       "5,123.6",
+		"=FIXED(5123.591,0)":       "5,124",
+		"=FIXED(5123.591,-1)":      "5,120",
+		"=FIXED(5123.591,-2)":      "5,100",
+		"=FIXED(5123.591,-3,TRUE)": "5000",
+		"=FIXED(5123.591,-5)":      "0",
+		"=FIXED(-77262.23973,-5)":  "-100,000",
 		// FIND
 		// FIND
 		"=FIND(\"T\",\"Original Text\")":   "10",
 		"=FIND(\"T\",\"Original Text\")":   "10",
 		"=FIND(\"t\",\"Original Text\")":   "13",
 		"=FIND(\"t\",\"Original Text\")":   "13",
@@ -1478,6 +1487,12 @@ func TestCalcCellValue(t *testing.T) {
 		// EXACT
 		// EXACT
 		"=EXACT()":      "EXACT requires 2 arguments",
 		"=EXACT()":      "EXACT requires 2 arguments",
 		"=EXACT(1,2,3)": "EXACT requires 2 arguments",
 		"=EXACT(1,2,3)": "EXACT requires 2 arguments",
+		// FIXED
+		"=FIXED()":         "FIXED requires at least 1 argument",
+		"=FIXED(0,1,2,3)":  "FIXED allows at most 3 arguments",
+		"=FIXED(\"\")":     "strconv.ParseFloat: parsing \"\": invalid syntax",
+		"=FIXED(0,\"\")":   "strconv.ParseFloat: parsing \"\": invalid syntax",
+		"=FIXED(0,0,\"\")": "strconv.ParseBool: parsing \"\": invalid syntax",
 		// FIND
 		// FIND
 		"=FIND()":                 "FIND requires at least 2 arguments",
 		"=FIND()":                 "FIND requires at least 2 arguments",
 		"=FIND(1,2,3,4)":          "FIND allows at most 3 arguments",
 		"=FIND(1,2,3,4)":          "FIND allows at most 3 arguments",

+ 3 - 3
go.mod

@@ -7,8 +7,8 @@ require (
 	github.com/richardlehane/mscfb v1.0.3
 	github.com/richardlehane/mscfb v1.0.3
 	github.com/stretchr/testify v1.6.1
 	github.com/stretchr/testify v1.6.1
 	github.com/xuri/efp v0.0.0-20210311002341-9c6784cb2d17
 	github.com/xuri/efp v0.0.0-20210311002341-9c6784cb2d17
-	golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
-	golang.org/x/image v0.0.0-20201208152932-35266b937fa6
-	golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
+	golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670
+	golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
+	golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4
 	golang.org/x/text v0.3.5
 	golang.org/x/text v0.3.5
 )
 )

+ 7 - 10
go.sum

@@ -13,18 +13,15 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/xuri/efp v0.0.0-20210311002341-9c6784cb2d17 h1:Ou4I7pYPQBk/qE9K2y31rawl/ftLHbTJJAFYJPVSyQo=
 github.com/xuri/efp v0.0.0-20210311002341-9c6784cb2d17 h1:Ou4I7pYPQBk/qE9K2y31rawl/ftLHbTJJAFYJPVSyQo=
 github.com/xuri/efp v0.0.0-20210311002341-9c6784cb2d17/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
 github.com/xuri/efp v0.0.0-20210311002341-9c6784cb2d17/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
-golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
-golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI=
-golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
+golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670 h1:gzMM0EjIYiRmJI3+jBdFuoynZlpxa2JQZsolKu09BXo=
+golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk=
+golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=