Merge pull request #11 from codeplea/POW_FROM_RIGHT

Pow from right
This commit is contained in:
Lewis Van Winkle
2016-08-23 15:36:03 -05:00
committed by GitHub
3 changed files with 139 additions and 2 deletions

View File

@@ -258,17 +258,39 @@ Valid variable names are any combination of the lower case letters *a* through
TinyExpr supports addition (+), subtraction/negation (-), multiplication (\*),
division (/), exponentiation (^) and modulus (%) with the normal operator
precedence (the one exception being that exponentiation is evaluated
left-to-right).
left-to-right, but this can be changed - see below).
In addition, the following C math functions are also supported:
- abs (calls to *fabs*), acos, asin, atan, atan2, ceil, cos, cosh, exp, floor, ln (calls to *log*), log (calls to *log10*), pow, sin, sinh, sqrt, tan, tanh
- abs (calls to *fabs*), acos, asin, atan, atan2, ceil, cos, cosh, exp, floor, ln (calls to *log*), log (calls to *log10* by default, see below), log10, pow, sin, sinh, sqrt, tan, tanh
Also, the following constants are available:
- `pi`, `e`
##Compile-time options
By default, TinyExpr does exponentation from left to right. For example:
`a^b^c == (a^b)^c` and `-a^b == (-a)^b`
This is by design. It's the way that spreadsheets do it (e.g. Excel, Google Sheets).
If you would rather have exponentation work from right to left, you need to
define `TE_POW_FROM_RIGHT` when compiling `tinyexpr.c`. There is a
commented-out define near the top of that file. With this option enabled, the
behaviour is:
`a^b^c == a^(b^c)` and `-a^b == -(a^b)`
That will match how many scripting languages do it (e.g. Python, Ruby).
Also, if you'd like `log` to default to the natural log instead of `log10`,
then you can define `TE_NAT_LOG`.
##Hints
- All functions/types start with the letters *te*.

57
test.c
View File

@@ -32,6 +32,10 @@ typedef struct {
double answer;
} test_case;
typedef struct {
const char *expr1;
const char *expr2;
} test_equ;
@@ -517,6 +521,58 @@ void test_optimize() {
}
}
void test_pow() {
#ifdef TE_POW_FROM_RIGHT
test_equ cases[] = {
{"2^3^4", "2^(3^4)"},
{"-2^2", "-(2^2)"},
{"-(2)^2", "-(2^2)"},
{"-(2*1)^2", "-(2^2)"},
{"-2^2", "-4"},
{"2^1.1^1.2^1.3", "2^(1.1^(1.2^1.3))"},
{"-a^b", "-(a^b)"},
{"-a^-b", "-(a^-b)"}
};
#else
test_equ cases[] = {
{"2^3^4", "(2^3)^4"},
{"-2^2", "(-2)^2"},
{"-2^2", "4"},
{"2^1.1^1.2^1.3", "((2^1.1)^1.2)^1.3"},
{"-a^b", "(-a)^b"},
{"-a^-b", "(-a)^(-b)"}
};
#endif
double a = 2, b = 3;
te_variable lookup[] = {
{"a", &a},
{"b", &b}
};
int i;
for (i = 0; i < sizeof(cases) / sizeof(test_equ); ++i) {
const char *expr1 = cases[i].expr1;
const char *expr2 = cases[i].expr2;
te_expr *ex1 = te_compile(expr1, lookup, sizeof(lookup)/sizeof(te_variable), 0);
te_expr *ex2 = te_compile(expr2, lookup, sizeof(lookup)/sizeof(te_variable), 0);
lok(ex1);
lok(ex2);
double r1 = te_eval(ex1);
double r2 = te_eval(ex2);
fflush(stdout);
lfequal(r1, r2);
te_free(ex1);
te_free(ex2);
}
}
int main(int argc, char *argv[])
{
@@ -528,6 +584,7 @@ int main(int argc, char *argv[])
lrun("Dynamic", test_dynamic);
lrun("Closure", test_closure);
lrun("Optimize", test_optimize);
lrun("Pow", test_pow);
lresults();
return lfails != 0;

View File

@@ -22,6 +22,18 @@
* 3. This notice may not be removed or altered from any source distribution.
*/
/* COMPILE TIME OPTIONS */
/* Exponentiation associativity:
For a^b^c = (a^b)^c and -a^b = (-a)^b do nothing.
For a^b^c = a^(b^c) and -a^b = -(a^b) uncomment the next line.*/
/* #define TE_POW_FROM_RIGHT */
/* Logarithms
For log = base 10 log do nothing
For log = natural log uncomment the next line. */
/* #define TE_NAT_LOG */
#include "tinyexpr.h"
#include <stdlib.h>
#include <math.h>
@@ -116,7 +128,12 @@ static const te_variable functions[] = {
{"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE},
{"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE},
{"ln", log, TE_FUNCTION1 | TE_FLAG_PURE},
#ifdef TE_NAT_LOG
{"log", log, TE_FUNCTION1 | TE_FLAG_PURE},
#else
{"log", log10, TE_FUNCTION1 | TE_FLAG_PURE},
#endif
{"log10", log10, TE_FUNCTION1 | TE_FLAG_PURE},
{"pi", pi, TE_FUNCTION0 | TE_FLAG_PURE},
{"pow", pow, TE_FUNCTION2 | TE_FLAG_PURE},
{"sin", sin, TE_FUNCTION1 | TE_FLAG_PURE},
@@ -357,7 +374,46 @@ static te_expr *power(state *s) {
return ret;
}
#ifdef TE_POW_FROM_RIGHT
static te_expr *factor(state *s) {
/* <factor> = <power> {"^" <power>} */
te_expr *ret = power(s);
int neg = 0;
te_expr *insertion = 0;
if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) {
te_expr *se = ret->parameters[0];
free(ret);
ret = se;
neg = 1;
}
while (s->type == TOK_INFIX && (s->function == pow)) {
te_fun2 t = s->function;
next_token(s);
if (insertion) {
/* Make exponentiation go right-to-left. */
te_expr *insert = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], power(s));
insert->function = t;
insertion->parameters[1] = insert;
insertion = insert;
} else {
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s));
ret->function = t;
insertion = ret;
}
}
if (neg) {
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret);
ret->function = negate;
}
return ret;
}
#else
static te_expr *factor(state *s) {
/* <factor> = <power> {"^" <power>} */
te_expr *ret = power(s);
@@ -371,6 +427,8 @@ static te_expr *factor(state *s) {
return ret;
}
#endif
static te_expr *term(state *s) {