Skip to content

Commit 424a7e9

Browse files
committed
Add EVEN, ODD, ISEVEN, and ISODD functions
These are the same as the Excel functions.
1 parent d8e24d9 commit 424a7e9

File tree

4 files changed

+130
-24
lines changed

4 files changed

+130
-24
lines changed

TinyExprChanges.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,19 @@ The following are changes from the original TinyExpr C library:
4646
- `cot`: returns the cotangent of an angle.
4747
- `combin`: alias for `ncr()`, like the *Excel* function.
4848
- `clamp`: constrains a value to a range.
49+
- `even`: returns a value rounded up to the nearest even integer.
4950
- `fact`: alias for `fac()`, like the *Excel* function.
5051
- `false`: returns `false` (i.e., `0`) in a boolean expression.
52+
- `iseven`: returns true if a number is even, false if odd.
53+
- `isodd`: returns true if a number is odd, false if even.
5154
- `if`: if a value is true (i.e., non-zero), then returns the second argument; otherwise, returns the third argument.
5255
- `ifs`: checks up to three conditions, returning the value corresponding to the first met condition.
5356
- `max`: returns the maximum of a range of values (accepts 1-7 arguments).
5457
- `maxint`: returns the largest integer value that the parser can store.
5558
- `min`: returns the minimum of a range of values (accepts 1-7 arguments).
5659
- `mod`: returns remainder from a division.
5760
- `nan`: returns `NaN` (i.e., Not-a-Number) in a boolean expression.
61+
- `odd`: returns a value rounded up to the nearest odd integer.
5862
- `or`: returns true (i.e., non-zero) if any condition is true (accepts 1-7 arguments).
5963
- `not`: returns logical negation of value.
6064
- `permut`: alias for `npr()`, like the *Excel* function.

docs/manual/functions.qmd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ The following built-in functions are available:
1717
| COSH(Number) | Hyperbolic cosine of *Number*. |
1818
| COT(Number) | Cotangent of *Number*. |
1919
| EXP(Number) | Euler to the power of *Number*. |
20+
| EVEN(Number) | Returns *Number* rounded up to the nearest even integer.<br>\linebreak *Number* is always rounded away from zero (e.g., `EVEN(-3)` = -4). |
2021
| FAC(Number) | Returns the factorial of *Number*. The factorial of *Number* is equal to 1\*2\*3\*...\* *Number* |
2122
| FACT(Number) | Alias for `FAC()` |
2223
| FLOOR(Number) | Returns the largest integer not greater than *Number*.<br>\linebreak `FLOOR(-3.2)` = -4<br>\linebreak `FLOOR(3.2)` = 3 |
24+
| ISEVEN(Number) | Returns true if *Number* is even, false if odd. |
25+
| ISODD(Number) | Returns true if *Number* is odd, false if even. |
2326
| LN(Number) | Natural logarithm of *Number* (base Euler). |
2427
| LOG10(Number) | Common logarithm of *Number* (base 10). |
2528
| MIN(Number1, Number2, ...) | Returns the smallest value from a specified range of values. |
@@ -29,6 +32,7 @@ The following built-in functions are available:
2932
| NAN | Returns an invalid value (i.e., Not-a-number). |
3033
| NCR(Number, NumberChosen) | Alias for `COMBIN()`. |
3134
| NPR(Number, NumberChosen) | Alias for `PERMUT()`. |
35+
| ODD(Number) | Returns *Number* rounded up to the nearest odd integer.<br>\linebreak *Number* is always rounded away from zero (e.g., `ODD(-4)` = -5). |
3236
| PERMUT(Number, NumberChosen) | Returns the number of permutations for a given number (*NumberChosen*) of items that can be selected *Number* of items. A permutation is any set of items where order is important. (This differs from combinations, where order is not important). |
3337
| POW(Base, Exponent) | Raises *Base* to any power. For fractional exponents, *Base* must be greater than 0. |
3438
| POWER(Base, Exponent) | Alias for `POW()`. |

tests/tetests.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,48 @@ TEST_CASE("NaN", "[nan]")
746746
CHECK(tep.success());
747747
}
748748

749+
TEST_CASE("Even", "[even]")
750+
{
751+
te_parser tep;
752+
753+
CHECK(tep.evaluate("=EVEN(1.5)") == 2);
754+
CHECK(tep.evaluate("=EVEN(3)") == 4);
755+
CHECK(tep.evaluate("=EVEN(2)") == 2);
756+
CHECK(tep.evaluate("=EVEN(-1)") == -2);
757+
CHECK(tep.evaluate("=EVEN(-3)") == -4);
758+
CHECK(tep.evaluate("=EVEN(-3.7)") == -4);
759+
CHECK(tep.evaluate("=EVEN(-3.1)") == -4);
760+
CHECK(tep.evaluate("=EVEN(0)") == 0);
761+
CHECK(std::isnan(tep.evaluate("=EVEN(NAN)")));
762+
763+
CHECK_FALSE(tep.evaluate("ISEVEN(-1)"));
764+
CHECK(tep.evaluate("=ISEVEN(2.5)"));
765+
CHECK_FALSE(tep.evaluate("=ISEVEN(5)"));
766+
CHECK(tep.evaluate("=ISEVEN(0)"));
767+
CHECK(tep.evaluate("=ISEVEN(40900)"));
768+
CHECK(std::isnan(tep.evaluate("=ISEVEN(NAN)")));
769+
}
770+
771+
TEST_CASE("Odd", "[odd]")
772+
{
773+
te_parser tep;
774+
775+
CHECK(tep.evaluate("=ODD(1.5)") == 3);
776+
CHECK(tep.evaluate("=ODD(3)") == 3);
777+
CHECK(tep.evaluate("=ODD(2)") == 3);
778+
CHECK(tep.evaluate("=ODD(-1)") == -1);
779+
CHECK(tep.evaluate("=ODD(-2)") == -3);
780+
CHECK(tep.evaluate("=ODD(-4)") == -5);
781+
CHECK(tep.evaluate("=ODD(0)") == 1);
782+
CHECK(std::isnan(tep.evaluate("=ODD(NAN)")));
783+
784+
CHECK(tep.evaluate("ISODD(-1)"));
785+
CHECK_FALSE(tep.evaluate("=ISODD(2.5)"));
786+
CHECK_FALSE(tep.evaluate("=ISODD(0)"));
787+
CHECK(tep.evaluate("=ISODD(5)"));
788+
CHECK(std::isnan(tep.evaluate("=ISODD(NAN)")));
789+
}
790+
749791
TEST_CASE("Zeros", "[zeros]")
750792
{
751793
te_parser tep;

tinyexpr.cpp

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,82 @@
5252
// builtin functions
5353
namespace te_builtins
5454
{
55+
[[nodiscard]]
56+
constexpr static te_type te_false_value() noexcept
57+
{
58+
return 0;
59+
}
60+
61+
[[nodiscard]]
62+
constexpr static te_type te_true_value() noexcept
63+
{
64+
return 1;
65+
}
66+
67+
[[nodiscard]]
68+
constexpr static te_type te_nan_value() noexcept
69+
{
70+
return te_parser::te_nan;
71+
}
72+
73+
[[nodiscard]]
74+
static te_type te_max_integer() noexcept
75+
{
76+
return te_parser::get_max_integer();
77+
}
78+
79+
[[nodiscard]]
80+
static te_type te_even(te_type val)
81+
{
82+
if (!std::isfinite(val))
83+
{
84+
return te_parser::te_nan;
85+
}
86+
int64_t rounded{ static_cast<int64_t>(std::ceil(std::abs(val))) };
87+
if ((rounded % 2) != 0)
88+
{
89+
++rounded;
90+
}
91+
return (val < 0 ? -(static_cast<te_type>(rounded)) : static_cast<te_type>(rounded));
92+
}
93+
94+
[[nodiscard]]
95+
static te_type te_odd(te_type val)
96+
{
97+
if (!std::isfinite(val))
98+
{
99+
return te_parser::te_nan;
100+
}
101+
int64_t rounded{ static_cast<int64_t>(std::ceil(std::abs(val))) };
102+
if ((rounded % 2) == 0)
103+
{
104+
++rounded;
105+
}
106+
return (val < 0 ? -(static_cast<te_type>(rounded)) : static_cast<te_type>(rounded));
107+
}
108+
109+
[[nodiscard]]
110+
static te_type te_is_even(te_type val)
111+
{
112+
if (!std::isfinite(val))
113+
{
114+
return te_parser::te_nan;
115+
}
116+
const int64_t floored{ static_cast<int64_t>(std::floor(val)) };
117+
return ((floored % 2) == 0 ? te_true_value() : te_false_value());
118+
}
119+
120+
[[nodiscard]]
121+
static te_type te_is_odd(te_type val)
122+
{
123+
if (!std::isfinite(val))
124+
{
125+
return te_parser::te_nan;
126+
}
127+
const int64_t floored{ static_cast<int64_t>(std::floor(val)) };
128+
return ((floored % 2) != 0 ? te_true_value() : te_false_value());
129+
}
130+
55131
[[nodiscard]]
56132
constexpr static te_type te_equal(te_type val1, te_type val2) noexcept
57133
{
@@ -1043,30 +1119,6 @@ namespace te_builtins
10431119
te_parser::te_nan;
10441120
}
10451121

1046-
[[nodiscard]]
1047-
constexpr static te_type te_false_value() noexcept
1048-
{
1049-
return 0;
1050-
}
1051-
1052-
[[nodiscard]]
1053-
constexpr static te_type te_true_value() noexcept
1054-
{
1055-
return 1;
1056-
}
1057-
1058-
[[nodiscard]]
1059-
constexpr static te_type te_nan_value() noexcept
1060-
{
1061-
return te_parser::te_nan;
1062-
}
1063-
1064-
[[nodiscard]]
1065-
static te_type te_max_integer() noexcept
1066-
{
1067-
return te_parser::get_max_integer();
1068-
}
1069-
10701122
[[nodiscard]]
10711123
constexpr static te_type te_supports_32bit() noexcept
10721124
{
@@ -1187,11 +1239,14 @@ const std::set<te_variable> te_parser::m_functions = { // NOLINT
11871239
{ "cosh", static_cast<te_fun1>(te_builtins::te_cosh), TE_PURE },
11881240
{ "cot", static_cast<te_fun1>(te_builtins::te_cot), TE_PURE },
11891241
{ "e", static_cast<te_fun0>(te_builtins::te_e), TE_PURE },
1242+
{ "even", static_cast<te_fun1>(te_builtins::te_even), TE_PURE },
11901243
{ "exp", static_cast<te_fun1>(te_builtins::te_exp), TE_PURE },
11911244
{ "fac", static_cast<te_fun1>(te_builtins::te_fac), TE_PURE },
11921245
{ "fact", static_cast<te_fun1>(te_builtins::te_fac), TE_PURE },
11931246
{ "false", static_cast<te_fun0>(te_builtins::te_false_value), TE_PURE },
11941247
{ "floor", static_cast<te_fun1>(te_builtins::te_floor), TE_PURE },
1248+
{ "iseven", static_cast<te_fun1>(te_builtins::te_is_even), TE_PURE },
1249+
{ "isodd", static_cast<te_fun1>(te_builtins::te_is_odd), TE_PURE },
11951250
{ "if", static_cast<te_fun3>(te_builtins::te_if), TE_PURE },
11961251
{ "ifs", static_cast<te_fun6>(te_builtins::te_ifs),
11971252
static_cast<te_variable_flags>(TE_PURE | TE_VARIADIC) },
@@ -1207,6 +1262,7 @@ const std::set<te_variable> te_parser::m_functions = { // NOLINT
12071262
{ "ncr", static_cast<te_fun2>(te_builtins::te_ncr), TE_PURE },
12081263
{ "not", static_cast<te_fun1>(te_builtins::te_not), TE_PURE },
12091264
{ "npr", static_cast<te_fun2>(te_builtins::te_npr), TE_PURE },
1265+
{ "odd", static_cast<te_fun1>(te_builtins::te_odd), TE_PURE },
12101266
{ "or", static_cast<te_fun7>(te_builtins::te_or_variadic),
12111267
static_cast<te_variable_flags>(TE_PURE | TE_VARIADIC) },
12121268
{ "permut", static_cast<te_fun2>(te_builtins::te_npr), TE_PURE },

0 commit comments

Comments
 (0)