Skip to content

Commit 08185c3

Browse files
committed
#65, fn: ROUND, ROUNDDOWN, ROUNDUP, SEC, SECH, SIN, SINH, SQRTPI, TAN, TANH, TRUNC
1 parent de34eca commit 08185c3

File tree

2 files changed

+339
-2
lines changed

2 files changed

+339
-2
lines changed

calc.go

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,6 +2078,141 @@ func (fn *formulaFuncs) ROMAN(argsList *list.List) (result string, err error) {
20782078
return
20792079
}
20802080

2081+
type roundMode byte
2082+
2083+
const (
2084+
closest roundMode = iota
2085+
down
2086+
up
2087+
)
2088+
2089+
// round rounds a supplied number up or down.
2090+
func (fn *formulaFuncs) round(number, digits float64, mode roundMode) float64 {
2091+
significance := 1.0
2092+
if digits > 0 {
2093+
significance = math.Pow(1/10.0, digits)
2094+
} else {
2095+
significance = math.Pow(10.0, -digits)
2096+
}
2097+
val, res := math.Modf(number / significance)
2098+
switch mode {
2099+
case closest:
2100+
const eps = 0.499999999
2101+
if res >= eps {
2102+
val++
2103+
} else if res <= -eps {
2104+
val--
2105+
}
2106+
case down:
2107+
case up:
2108+
if res > 0 {
2109+
val++
2110+
} else if res < 0 {
2111+
val--
2112+
}
2113+
}
2114+
return val * significance
2115+
}
2116+
2117+
// ROUND function rounds a supplied number up or down, to a specified number
2118+
// of decimal places. The syntax of the function is:
2119+
//
2120+
// ROUND(number,num_digits)
2121+
//
2122+
func (fn *formulaFuncs) ROUND(argsList *list.List) (result string, err error) {
2123+
if argsList.Len() != 2 {
2124+
err = errors.New("ROUND requires 2 numeric arguments")
2125+
return
2126+
}
2127+
var number, digits float64
2128+
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
2129+
return
2130+
}
2131+
if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil {
2132+
return
2133+
}
2134+
result = fmt.Sprintf("%g", fn.round(number, digits, closest))
2135+
return
2136+
}
2137+
2138+
// ROUNDDOWN function rounds a supplied number down towards zero, to a
2139+
// specified number of decimal places. The syntax of the function is:
2140+
//
2141+
// ROUNDDOWN(number,num_digits)
2142+
//
2143+
func (fn *formulaFuncs) ROUNDDOWN(argsList *list.List) (result string, err error) {
2144+
if argsList.Len() != 2 {
2145+
err = errors.New("ROUNDDOWN requires 2 numeric arguments")
2146+
return
2147+
}
2148+
var number, digits float64
2149+
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
2150+
return
2151+
}
2152+
if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil {
2153+
return
2154+
}
2155+
result = fmt.Sprintf("%g", fn.round(number, digits, down))
2156+
return
2157+
}
2158+
2159+
// ROUNDUP function rounds a supplied number up, away from zero, to a
2160+
// specified number of decimal places. The syntax of the function is:
2161+
//
2162+
// ROUNDUP(number,num_digits)
2163+
//
2164+
func (fn *formulaFuncs) ROUNDUP(argsList *list.List) (result string, err error) {
2165+
if argsList.Len() != 2 {
2166+
err = errors.New("ROUNDUP requires 2 numeric arguments")
2167+
return
2168+
}
2169+
var number, digits float64
2170+
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
2171+
return
2172+
}
2173+
if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil {
2174+
return
2175+
}
2176+
result = fmt.Sprintf("%g", fn.round(number, digits, up))
2177+
return
2178+
}
2179+
2180+
// SEC function calculates the secant of a given angle. The syntax of the
2181+
// function is:
2182+
//
2183+
// SEC(number)
2184+
//
2185+
func (fn *formulaFuncs) SEC(argsList *list.List) (result string, err error) {
2186+
if argsList.Len() != 1 {
2187+
err = errors.New("SEC requires 1 numeric argument")
2188+
return
2189+
}
2190+
var number float64
2191+
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
2192+
return
2193+
}
2194+
result = fmt.Sprintf("%g", math.Cos(number))
2195+
return
2196+
}
2197+
2198+
// SECH function calculates the hyperbolic secant (sech) of a supplied angle.
2199+
// The syntax of the function is:
2200+
//
2201+
// SECH(number)
2202+
//
2203+
func (fn *formulaFuncs) SECH(argsList *list.List) (result string, err error) {
2204+
if argsList.Len() != 1 {
2205+
err = errors.New("SECH requires 1 numeric argument")
2206+
return
2207+
}
2208+
var number float64
2209+
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
2210+
return
2211+
}
2212+
result = fmt.Sprintf("%g", 1/math.Cosh(number))
2213+
return
2214+
}
2215+
20812216
// SIGN function returns the arithmetic sign (+1, -1 or 0) of a supplied
20822217
// number. I.e. if the number is positive, the Sign function returns +1, if
20832218
// the number is negative, the function returns -1 and if the number is 0
@@ -2106,6 +2241,42 @@ func (fn *formulaFuncs) SIGN(argsList *list.List) (result string, err error) {
21062241
return
21072242
}
21082243

2244+
// SIN function calculates the sine of a given angle. The syntax of the
2245+
// function is:
2246+
//
2247+
// SIN(number)
2248+
//
2249+
func (fn *formulaFuncs) SIN(argsList *list.List) (result string, err error) {
2250+
if argsList.Len() != 1 {
2251+
err = errors.New("SIN requires 1 numeric argument")
2252+
return
2253+
}
2254+
var number float64
2255+
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
2256+
return
2257+
}
2258+
result = fmt.Sprintf("%g", math.Sin(number))
2259+
return
2260+
}
2261+
2262+
// SINH function calculates the hyperbolic sine (sinh) of a supplied number.
2263+
// The syntax of the function is:
2264+
//
2265+
// SINH(number)
2266+
//
2267+
func (fn *formulaFuncs) SINH(argsList *list.List) (result string, err error) {
2268+
if argsList.Len() != 1 {
2269+
err = errors.New("SINH requires 1 numeric argument")
2270+
return
2271+
}
2272+
var number float64
2273+
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
2274+
return
2275+
}
2276+
result = fmt.Sprintf("%g", math.Sinh(number))
2277+
return
2278+
}
2279+
21092280
// SQRT function calculates the positive square root of a supplied number. The
21102281
// syntax of the function is:
21112282
//
@@ -2133,6 +2304,24 @@ func (fn *formulaFuncs) SQRT(argsList *list.List) (result string, err error) {
21332304
return
21342305
}
21352306

2307+
// SQRTPI function returns the square root of a supplied number multiplied by
2308+
// the mathematical constant, π. The syntax of the function is:
2309+
//
2310+
// SQRTPI(number)
2311+
//
2312+
func (fn *formulaFuncs) SQRTPI(argsList *list.List) (result string, err error) {
2313+
if argsList.Len() != 1 {
2314+
err = errors.New("SQRTPI requires 1 numeric argument")
2315+
return
2316+
}
2317+
var number float64
2318+
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
2319+
return
2320+
}
2321+
result = fmt.Sprintf("%g", math.Sqrt(number*math.Pi))
2322+
return
2323+
}
2324+
21362325
// SUM function adds together a supplied set of numbers and returns the sum of
21372326
// these values. The syntax of the function is:
21382327
//
@@ -2153,3 +2342,74 @@ func (fn *formulaFuncs) SUM(argsList *list.List) (result string, err error) {
21532342
result = fmt.Sprintf("%g", sum)
21542343
return
21552344
}
2345+
2346+
// TAN function calculates the tangent of a given angle. The syntax of the
2347+
// function is:
2348+
//
2349+
// TAN(number)
2350+
//
2351+
func (fn *formulaFuncs) TAN(argsList *list.List) (result string, err error) {
2352+
if argsList.Len() != 1 {
2353+
err = errors.New("TAN requires 1 numeric argument")
2354+
return
2355+
}
2356+
var number float64
2357+
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
2358+
return
2359+
}
2360+
result = fmt.Sprintf("%g", math.Tan(number))
2361+
return
2362+
}
2363+
2364+
// TANH function calculates the hyperbolic tangent (tanh) of a supplied
2365+
// number. The syntax of the function is:
2366+
//
2367+
// TANH(number)
2368+
//
2369+
func (fn *formulaFuncs) TANH(argsList *list.List) (result string, err error) {
2370+
if argsList.Len() != 1 {
2371+
err = errors.New("TANH requires 1 numeric argument")
2372+
return
2373+
}
2374+
var number float64
2375+
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
2376+
return
2377+
}
2378+
result = fmt.Sprintf("%g", math.Tanh(number))
2379+
return
2380+
}
2381+
2382+
// TRUNC function truncates a supplied number to a specified number of decimal
2383+
// places. The syntax of the function is:
2384+
//
2385+
// TRUNC(number,[number_digits])
2386+
//
2387+
func (fn *formulaFuncs) TRUNC(argsList *list.List) (result string, err error) {
2388+
if argsList.Len() == 0 {
2389+
err = errors.New("TRUNC requires at least 1 argument")
2390+
return
2391+
}
2392+
var number, digits, adjust, rtrim float64
2393+
if number, err = strconv.ParseFloat(argsList.Front().Value.(formulaArg).Value, 64); err != nil {
2394+
return
2395+
}
2396+
if argsList.Len() > 1 {
2397+
if digits, err = strconv.ParseFloat(argsList.Back().Value.(formulaArg).Value, 64); err != nil {
2398+
return
2399+
}
2400+
digits = math.Floor(digits)
2401+
}
2402+
adjust = math.Pow(10, digits)
2403+
x := int((math.Abs(number) - math.Abs(float64(int(number)))) * adjust)
2404+
if x != 0 {
2405+
if rtrim, err = strconv.ParseFloat(strings.TrimRight(strconv.Itoa(x), "0"), 64); err != nil {
2406+
return
2407+
}
2408+
}
2409+
if (digits > 0) && (rtrim < adjust/10) {
2410+
result = fmt.Sprintf("%g", number)
2411+
return
2412+
}
2413+
result = fmt.Sprintf("%g", float64(int(number*adjust))/adjust)
2414+
return
2415+
}

calc_test.go

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,14 +266,55 @@ func TestCalcCellValue(t *testing.T) {
266266
"=ROMAN(1999,2)": "MXMIX",
267267
"=ROMAN(1999,3)": "MVMIV",
268268
"=ROMAN(1999,4)": "MIM",
269+
// ROUND
270+
"=ROUND(100.319,1)": "100.30000000000001",
271+
"=ROUND(5.28,1)": "5.300000000000001",
272+
"=ROUND(5.9999,3)": "6.000000000000002",
273+
"=ROUND(99.5,0)": "100",
274+
"=ROUND(-6.3,0)": "-6",
275+
"=ROUND(-100.5,0)": "-101",
276+
"=ROUND(-22.45,1)": "-22.5",
277+
"=ROUND(999,-1)": "1000",
278+
"=ROUND(991,-1)": "990",
279+
// ROUNDDOWN
280+
"=ROUNDDOWN(99.999,1)": "99.9",
281+
"=ROUNDDOWN(99.999,2)": "99.99000000000002",
282+
"=ROUNDDOWN(99.999,0)": "99",
283+
"=ROUNDDOWN(99.999,-1)": "90",
284+
"=ROUNDDOWN(-99.999,2)": "-99.99000000000002",
285+
"=ROUNDDOWN(-99.999,-1)": "-90",
286+
// ROUNDUP
287+
"=ROUNDUP(11.111,1)": "11.200000000000001",
288+
"=ROUNDUP(11.111,2)": "11.120000000000003",
289+
"=ROUNDUP(11.111,0)": "12",
290+
"=ROUNDUP(11.111,-1)": "20",
291+
"=ROUNDUP(-11.111,2)": "-11.120000000000003",
292+
"=ROUNDUP(-11.111,-1)": "-20",
293+
// SEC
294+
"=_xlfn.SEC(-3.14159265358979)": "-1",
295+
"=_xlfn.SEC(0)": "1",
296+
// SECH
297+
"=_xlfn.SECH(-3.14159265358979)": "0.0862667383340547",
298+
"=_xlfn.SECH(0)": "1",
269299
// SIGN
270300
"=SIGN(9.5)": "1",
271301
"=SIGN(-9.5)": "-1",
272302
"=SIGN(0)": "0",
273303
"=SIGN(0.00000001)": "1",
274304
"=SIGN(6-7)": "-1",
305+
// SIN
306+
"=SIN(0.785398163)": "0.7071067809055092",
307+
// SINH
308+
"=SINH(0)": "0",
309+
"=SINH(0.5)": "0.5210953054937474",
310+
"=SINH(-2)": "-3.626860407847019",
275311
// SQRT
276312
"=SQRT(4)": "2",
313+
// SQRTPI
314+
"=SQRTPI(5)": "3.963327297606011",
315+
"=SQRTPI(0.2)": "0.7926654595212022",
316+
"=SQRTPI(100)": "17.72453850905516",
317+
"=SQRTPI(0)": "0",
277318
// SUM
278319
"=SUM(1,2)": "3",
279320
"=SUM(1,2+3)": "6",
@@ -288,6 +329,20 @@ func TestCalcCellValue(t *testing.T) {
288329
"=((3+5*2)+3)/5+(-6)/4*2+3": "3.2",
289330
"=1+SUM(SUM(1,2*3),4)*-4/2+5+(4+2)*3": "2",
290331
"=1+SUM(SUM(1,2*3),4)*4/3+5+(4+2)*3": "38.666666666666664",
332+
// TAN
333+
"=TAN(1.047197551)": "1.732050806782486",
334+
"=TAN(0)": "0",
335+
// TANH
336+
"=TANH(0)": "0",
337+
"=TANH(0.5)": "0.46211715726000974",
338+
"=TANH(-2)": "-0.9640275800758169",
339+
// TRUNC
340+
"=TRUNC(99.999,1)": "99.9",
341+
"=TRUNC(99.999,2)": "99.99",
342+
"=TRUNC(99.999)": "99",
343+
"=TRUNC(99.999,-1)": "90",
344+
"=TRUNC(-99.999,2)": "-99.99",
345+
"=TRUNC(-99.999,-1)": "-90",
291346
}
292347
for formula, expected := range mathCalc {
293348
f := prepareData()
@@ -431,11 +486,33 @@ func TestCalcCellValue(t *testing.T) {
431486
// ROMAN
432487
"=ROMAN()": "ROMAN requires at least 1 argument",
433488
"=ROMAN(1,2,3)": "ROMAN allows at most 2 arguments",
489+
// ROUND
490+
"=ROUND()": "ROUND requires 2 numeric arguments",
491+
// ROUNDDOWN
492+
"=ROUNDDOWN()": "ROUNDDOWN requires 2 numeric arguments",
493+
// ROUNDUP
494+
"=ROUNDUP()": "ROUNDUP requires 2 numeric arguments",
495+
// SEC
496+
"=_xlfn.SEC()": "SEC requires 1 numeric argument",
497+
// _xlfn.SECH
498+
"=_xlfn.SECH()": "SECH requires 1 numeric argument",
434499
// SIGN
435500
"=SIGN()": "SIGN requires 1 numeric argument",
501+
// SIN
502+
"=SIN()": "SIN requires 1 numeric argument",
503+
// SINH
504+
"=SINH()": "SINH requires 1 numeric argument",
436505
// SQRT
437-
"=SQRT(-1)": "#NUM!",
438-
"=SQRT(1,2)": "SQRT requires 1 numeric argument",
506+
"=SQRT()": "SQRT requires 1 numeric argument",
507+
"=SQRT(-1)": "#NUM!",
508+
// SQRTPI
509+
"=SQRTPI()": "SQRTPI requires 1 numeric argument",
510+
// TAN
511+
"=TAN()": "TAN requires 1 numeric argument",
512+
// TANH
513+
"=TANH()": "TANH requires 1 numeric argument",
514+
// TRUNC
515+
"=TRUNC()": "TRUNC requires at least 1 argument",
439516
}
440517
for formula, expected := range mathCalcError {
441518
f := prepareData()

0 commit comments

Comments
 (0)