void test_nans() { const char *nans[] = { "0/0", "1%0", "1%(1%0)", "(1%0)%1", }; int i; for (i = 0; i < sizeof(nans) / sizeof(const char *); ++i) { const char *expr = nans[i]; int err; const double r = te_interp(expr, &err); lequal(err, 0); lok(r != r); te_expr *n = te_compile(expr, 0, 0, &err); lok(n); lequal(err, 0); const double c = te_eval(n); lok(c != c); te_free(n); } }
TEST(tinyexpr_suite, testNask) { int error; const double ecx1 = te_interp("512*1024/4", &error); CHECK(!std::isnan(ecx1)); CHECK_EQUAL(131072, ecx1); const double ecx2 = te_interp("512/4", &error); CHECK(!std::isnan(ecx2)); CHECK_EQUAL(128, ecx2); const double dw = te_interp("8*3-1", &error); CHECK(!std::isnan(dw)); CHECK_EQUAL(23, dw); }
TEST(tinyexpr_suite, testInt) { int error; /* Returns 10. */ const double a = te_interp("(5+5)", 0); CHECK_EQUAL(10, a); /* Returns 10, error is set to 0. */ const double b = te_interp("(5+5)", &error); CHECK_EQUAL(10, b); CHECK_EQUAL(0, error); /* Returns NaN, error is set to 4. */ const double c = te_interp("(5+5", &error); CHECK(std::isnan(c)); CHECK_EQUAL(4, error); }
void test_syntax() { test_case errors[] = { {"", 1}, {"1+", 2}, {"1)", 2}, {"(1", 2}, {"1**1", 3}, {"1*2(+4", 4}, {"1*2(1+4", 4}, {"a+5", 1}, {"A+5", 1}, {"Aa+5", 1}, {"1^^5", 3}, {"1**5", 3}, {"sin(cos5", 8}, }; int i; for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) { const char *expr = errors[i].expr; const int e = errors[i].answer; int err; const double r = te_interp(expr, &err); lequal(err, e); lok(r != r); te_expr *n = te_compile(expr, 0, 0, &err); lequal(err, e); lok(!n); if (err != e) { printf("FAILED: %s\n", expr); } const double k = te_interp(expr, 0); lok(k != k); } }
/// Evaluate math expressions. static int evaluate_expression(const wchar_t *cmd, parser_t &parser, io_streams_t &streams, math_cmd_opts_t &opts, wcstring &expression) { UNUSED(parser); int retval = STATUS_CMD_OK; te_error_t error; std::string narrow_str = wcs2string(expression); // Switch locale while computing stuff. // This means that the "." is always the radix character, // so numbers work the same across locales. char *saved_locale = strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); double v = te_interp(narrow_str.c_str(), &error); if (error.position == 0) { // Check some runtime errors after the fact. // TODO: Really, this should be done in tinyexpr // (e.g. infinite is the result of "x / 0"), // but that's much more work. const char *error_message = NULL; if (std::isinf(v)) { error_message = "Result is infinite"; } else if (std::isnan(v)) { error_message = "Result is not a number"; } else if (std::abs(v) >= kMaximumContiguousInteger) { error_message = "Result magnitude is too large"; } if (error_message) { streams.err.append_format(L"%ls: Error: %s\n", cmd, error_message); streams.err.append_format(L"'%ls'\n", expression.c_str()); retval = STATUS_CMD_ERROR; } else { streams.out.append(format_double(v, opts)); streams.out.push_back(L'\n'); } } else { streams.err.append_format(L"%ls: Error: %ls\n", cmd, math_describe_error(error).c_str()); streams.err.append_format(L"'%ls'\n", expression.c_str()); streams.err.append_format(L"%*ls%ls\n", error.position - 1, L" ",L"^"); retval = STATUS_CMD_ERROR; } setlocale(LC_NUMERIC, saved_locale); free(saved_locale); return retval; }
void test_results() { test_case cases[] = { {"1", 1}, {"1 ", 1}, {"(1)", 1}, {"pi", 3.14159}, {"atan(1)*4 - pi", 0}, {"e", 2.71828}, {"2+1", 2+1}, {"(((2+(1))))", 2+1}, {"3+2", 3+2}, {"3+2+4", 3+2+4}, {"(3+2)+4", 3+2+4}, {"3+(2+4)", 3+2+4}, {"(3+2+4)", 3+2+4}, {"3*2*4", 3*2*4}, {"(3*2)*4", 3*2*4}, {"3*(2*4)", 3*2*4}, {"(3*2*4)", 3*2*4}, {"3-2-4", 3-2-4}, {"(3-2)-4", (3-2)-4}, {"3-(2-4)", 3-(2-4)}, {"(3-2-4)", 3-2-4}, {"3/2/4", 3.0/2.0/4.0}, {"(3/2)/4", (3.0/2.0)/4.0}, {"3/(2/4)", 3.0/(2.0/4.0)}, {"(3/2/4)", 3.0/2.0/4.0}, {"(3*2/4)", 3.0*2.0/4.0}, {"(3/2*4)", 3.0/2.0*4.0}, {"3*(2/4)", 3.0*(2.0/4.0)}, {"asin sin .5", 0.5}, {"sin asin .5", 0.5}, {"ln exp .5", 0.5}, {"exp ln .5", 0.5}, {"asin sin-.5", -0.5}, {"asin sin-0.5", -0.5}, {"asin sin -0.5", -0.5}, {"asin (sin -0.5)", -0.5}, {"asin (sin (-0.5))", -0.5}, {"asin sin (-0.5)", -0.5}, {"(asin sin (-0.5))", -0.5}, {"log10 1000", 3}, {"log10 1e3", 3}, {"log10 1000", 3}, {"log10 1e3", 3}, {"log10(1000)", 3}, {"log10(1e3)", 3}, {"log10 1.0e3", 3}, {"10^5*5e-5", 5}, #ifdef TE_NAT_LOG {"log 1000", 6.9078}, {"log e", 1}, {"log (e^10)", 10}, #else {"log 1000", 3}, #endif {"ln (e^10)", 10}, {"100^.5+1", 11}, {"100 ^.5+1", 11}, {"100^+.5+1", 11}, {"100^--.5+1", 11}, {"100^---+-++---++-+-+-.5+1", 11}, {"100^-.5+1", 1.1}, {"100^---.5+1", 1.1}, {"100^+---.5+1", 1.1}, {"1e2^+---.5e0+1e0", 1.1}, {"--(1e2^(+(-(-(-.5e0))))+1e0)", 1.1}, {"sqrt 100 + 7", 17}, {"sqrt 100 * 7", 70}, {"sqrt (100 * 100)", 100}, {"1,2", 2}, {"1,2+1", 3}, {"1+1,2+2,2+1", 3}, {"1,2,3", 3}, {"(1,2),3", 3}, {"1,(2,3)", 3}, {"-(1,(2,3))", -3}, {"2^2", 4}, {"pow(2,2)", 4}, {"atan2(1,1)", 0.7854}, {"atan2(1,2)", 0.4636}, {"atan2(2,1)", 1.1071}, {"atan2(3,4)", 0.6435}, {"atan2(3+3,4*2)", 0.6435}, {"atan2(3+3,(4*2))", 0.6435}, {"atan2((3+3),4*2)", 0.6435}, {"atan2((3+3),(4*2))", 0.6435}, }; int i; for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char *expr = cases[i].expr; const double answer = cases[i].answer; int err; const double ev = te_interp(expr, &err); lok(!err); lfequal(ev, answer); if (err) { printf("FAILED: %s (%d)\n", expr, err); } } }