فهرست منبع

#65 fn: DEC2BIN, DEC2HEX, DEC2OCT

xuri 4 سال پیش
والد
کامیت
ae6f56b953
3فایلهای تغییر یافته به همراه144 افزوده شده و 2 حذف شده
  1. 1 0
      .travis.yml
  2. 99 2
      calc.go
  3. 44 0
      calc_test.go

+ 1 - 0
.travis.yml

@@ -9,6 +9,7 @@ go:
   - 1.13.x
   - 1.14.x
   - 1.15.x
+  - 1.16.x
 
 os:
   - linux

+ 99 - 2
calc.go

@@ -26,6 +26,7 @@ import (
 	"strings"
 	"time"
 	"unicode"
+	"unsafe"
 
 	"github.com/xuri/efp"
 )
@@ -240,6 +241,9 @@ var tokenPriority = map[string]int{
 //    CSC
 //    CSCH
 //    DATE
+//    DEC2BIN
+//    DEC2HEX
+//    DEC2OCT
 //    DECIMAL
 //    DEGREES
 //    ENCODEURL
@@ -1140,7 +1144,100 @@ func formulaCriteriaEval(val string, criteria *formulaCriteria) (result bool, er
 	return
 }
 
-// Math and Trigonometric functions
+// Engineering Functions
+
+// DEC2BIN function converts a decimal number into a Binary (Base 2) number.
+// The syntax of the function is:
+//
+//    DEC2BIN(number,[places])
+//
+func (fn *formulaFuncs) DEC2BIN(argsList *list.List) formulaArg {
+	if argsList.Len() < 1 {
+		return newErrorFormulaArg(formulaErrorVALUE, "DEC2BIN requires at least 1 argument")
+	}
+	if argsList.Len() > 2 {
+		return newErrorFormulaArg(formulaErrorVALUE, "DEC2BIN allows at most 2 arguments")
+	}
+	return fn.dec2x("DEC2BIN", argsList)
+}
+
+// DEC2HEX function converts a decimal number into a Hexadecimal (Base 16)
+// number. The syntax of the function is:
+//
+//    DEC2HEX(number,[places])
+//
+func (fn *formulaFuncs) DEC2HEX(argsList *list.List) formulaArg {
+	if argsList.Len() < 1 {
+		return newErrorFormulaArg(formulaErrorVALUE, "DEC2HEX requires at least 1 argument")
+	}
+	if argsList.Len() > 2 {
+		return newErrorFormulaArg(formulaErrorVALUE, "DEC2HEX allows at most 2 arguments")
+	}
+	return fn.dec2x("DEC2HEX", argsList)
+}
+
+// DEC2OCT function converts a decimal number into an Octal (Base 8) number.
+// The syntax of the function is:
+//
+//    DEC2OCT(number,[places])
+//
+func (fn *formulaFuncs) DEC2OCT(argsList *list.List) formulaArg {
+	if argsList.Len() < 1 {
+		return newErrorFormulaArg(formulaErrorVALUE, "DEC2OCT requires at least 1 argument")
+	}
+	if argsList.Len() > 2 {
+		return newErrorFormulaArg(formulaErrorVALUE, "DEC2OCT allows at most 2 arguments")
+	}
+	return fn.dec2x("DEC2OCT", argsList)
+}
+
+// dec2x is an implementation of the formula function DEC2BIN, DEC2HEX and DEC2OCT.
+func (fn *formulaFuncs) dec2x(name string, argsList *list.List) formulaArg {
+	decimal := argsList.Front().Value.(formulaArg).ToNumber()
+	if decimal.Type != ArgNumber {
+		return newErrorFormulaArg(formulaErrorVALUE, decimal.Error)
+	}
+	maxLimitMap := map[string]float64{
+		"DEC2BIN": 511,
+		"DEC2HEX": 549755813887,
+		"DEC2OCT": 536870911,
+	}
+	minLimitMap := map[string]float64{
+		"DEC2BIN": -512,
+		"DEC2HEX": -549755813888,
+		"DEC2OCT": -536870912,
+	}
+	baseMap := map[string]int{
+		"DEC2BIN": 2,
+		"DEC2HEX": 16,
+		"DEC2OCT": 8,
+	}
+	maxLimit := maxLimitMap[name]
+	minLimit := minLimitMap[name]
+	base := baseMap[name]
+	if decimal.Number < minLimit || decimal.Number > maxLimit {
+		return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
+	}
+	n := int64(decimal.Number)
+	binary := strconv.FormatUint(*(*uint64)(unsafe.Pointer(&n)), base)
+	if argsList.Len() == 2 {
+		places := argsList.Back().Value.(formulaArg).ToNumber()
+		if places.Type != ArgNumber {
+			return newErrorFormulaArg(formulaErrorVALUE, places.Error)
+		}
+		binaryPlaces := len(binary)
+		if places.Number < 0 || places.Number > 10 || binaryPlaces > int(places.Number) {
+			return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
+		}
+		return newStringFormulaArg(strings.ToUpper(fmt.Sprintf("%s%s", strings.Repeat("0", int(places.Number)-binaryPlaces), binary)))
+	}
+	if decimal.Number < 0 && len(binary) > 10 {
+		return newStringFormulaArg(strings.ToUpper(binary[len(binary)-10:]))
+	}
+	return newStringFormulaArg(strings.ToUpper(binary))
+}
+
+// Math and Trigonometric Functions
 
 // ABS function returns the absolute value of any supplied number. The syntax
 // of the function is:
@@ -4357,7 +4454,7 @@ func (fn *formulaFuncs) IF(argsList *list.List) formulaArg {
 	return newStringFormulaArg(result)
 }
 
-// Excel Lookup and Reference Functions
+// Lookup and Reference Functions
 
 // CHOOSE function returns a value from an array, that corresponds to a
 // supplied index number (position). The syntax of the function is:

+ 44 - 0
calc_test.go

@@ -46,6 +46,25 @@ func TestCalcCellValue(t *testing.T) {
 		"=2>=1": "TRUE",
 		"=2>=3": "FALSE",
 		"=1&2":  "12",
+		// Engineering Functions
+		// DEC2BIN
+		"=DEC2BIN(2)":    "10",
+		"=DEC2BIN(3)":    "11",
+		"=DEC2BIN(2,10)": "0000000010",
+		"=DEC2BIN(-2)":   "1111111110",
+		"=DEC2BIN(6)":    "110",
+		// DEC2HEX
+		"=DEC2HEX(10)":    "A",
+		"=DEC2HEX(31)":    "1F",
+		"=DEC2HEX(16,10)": "0000000010",
+		"=DEC2HEX(-16)":   "FFFFFFFFF0",
+		"=DEC2HEX(273)":   "111",
+		// DEC2OCT
+		"=DEC2OCT(8)":    "10",
+		"=DEC2OCT(18)":   "22",
+		"=DEC2OCT(8,10)": "0000000010",
+		"=DEC2OCT(-8)":   "7777777770",
+		"=DEC2OCT(237)":  "355",
 		// ABS
 		"=ABS(-1)":      "1",
 		"=ABS(-6.5)":    "6.5",
@@ -707,6 +726,31 @@ func TestCalcCellValue(t *testing.T) {
 	}
 	mathCalcError := map[string]string{
 		"=1/0": "#DIV/0!",
+		// Engineering Functions
+		// DEC2BIN
+		"=DEC2BIN()":        "DEC2BIN requires at least 1 argument",
+		"=DEC2BIN(1,1,1)":   "DEC2BIN allows at most 2 arguments",
+		"=DEC2BIN(\"\",1)":  "strconv.ParseFloat: parsing \"\": invalid syntax",
+		"=DEC2BIN(1,\"\")":  "strconv.ParseFloat: parsing \"\": invalid syntax",
+		"=DEC2BIN(-513,10)": "#NUM!",
+		"=DEC2BIN(1,-1)":    "#NUM!",
+		"=DEC2BIN(2,1)":     "#NUM!",
+		// DEC2HEX
+		"=DEC2HEX()":                 "DEC2HEX requires at least 1 argument",
+		"=DEC2HEX(1,1,1)":            "DEC2HEX allows at most 2 arguments",
+		"=DEC2HEX(\"\",1)":           "strconv.ParseFloat: parsing \"\": invalid syntax",
+		"=DEC2HEX(1,\"\")":           "strconv.ParseFloat: parsing \"\": invalid syntax",
+		"=DEC2HEX(-549755813888,10)": "#NUM!",
+		"=DEC2HEX(1,-1)":             "#NUM!",
+		"=DEC2HEX(31,1)":             "#NUM!",
+		// DEC2OCT
+		"=DEC2OCT()":               "DEC2OCT requires at least 1 argument",
+		"=DEC2OCT(1,1,1)":          "DEC2OCT allows at most 2 arguments",
+		"=DEC2OCT(\"\",1)":         "strconv.ParseFloat: parsing \"\": invalid syntax",
+		"=DEC2OCT(1,\"\")":         "strconv.ParseFloat: parsing \"\": invalid syntax",
+		"=DEC2OCT(-536870912 ,10)": "#NUM!",
+		"=DEC2OCT(1,-1)":           "#NUM!",
+		"=DEC2OCT(8,1)":            "#NUM!",
 		// ABS
 		"=ABS()":    "ABS requires 1 numeric argument",
 		`=ABS("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax",