From 6c37419d624a7aaec981807aa5fb098f96ccf763 Mon Sep 17 00:00:00 2001 From: Arash Partow Date: Tue, 6 Sep 2016 08:27:19 +1000 Subject: [PATCH] C++ Mathematical Expression Library (ExprTk) https://www.partow.net/programming/exprtk/index.html --- .travis.yml | 12 --- exprtk.hpp | 183 ++++++++++++++++++++--------------- exprtk_benchmark.cpp | 2 +- exprtk_simple_example_02.cpp | 2 +- exprtk_simple_example_11.cpp | 2 +- exprtk_simple_example_13.cpp | 2 +- exprtk_simple_example_17.cpp | 5 +- exprtk_test.cpp | 29 ++++-- readme.txt | 47 ++++++++- 9 files changed, 175 insertions(+), 109 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5ce5a53..0000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: cpp - -sudo: required - -dist: trusty - -compiler: - - gcc - -script: - - make clean all - - ./exprtk_test diff --git a/exprtk.hpp b/exprtk.hpp index acfa85c..dcec627 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -1111,13 +1111,21 @@ namespace exprtk return T(0); } + #if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || !defined(_MSC_VER) + #define exprtk_define_erf(TT,impl) \ + inline TT erf_impl(TT v) { return impl(v); } \ + + exprtk_define_erf( float,::erff) + exprtk_define_erf( double,::erf ) + exprtk_define_erf(long double,::erfl) + #undef exprtk_define_erf + #endif + template inline T erf_impl(T v, real_type_tag) { - #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + #if defined(_MSC_VER) && (_MSC_VER < 1900) // Credits: Abramowitz & Stegun Equations 7.1.25-28 - const T t = T(1) / (T(1) + T(0.5) * abs_impl(v,real_type_tag())); - static const T c[] = { T( 1.26551223), T(1.00002368), T( 0.37409196), T(0.09678418), @@ -1126,6 +1134,8 @@ namespace exprtk T(-0.82215223), T(0.17087277) }; + const T t = T(1) / (T(1) + T(0.5) * abs_impl(v,real_type_tag())); + T result = T(1) - t * std::exp((-v * v) - c[0] + t * (c[1] + t * (c[2] + t * (c[3] + t * @@ -1135,7 +1145,7 @@ namespace exprtk return (v >= T(0)) ? result : -result; #else - return ::erf(v); + return erf_impl(v); #endif } @@ -1145,13 +1155,23 @@ namespace exprtk return erf_impl(static_cast(v),real_type_tag()); } + #if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || !defined(_MSC_VER) + #define exprtk_define_erfc(TT,impl) \ + inline TT erfc_impl(TT v) { return impl(v); } \ + + exprtk_define_erfc( float,::erfcf) + exprtk_define_erfc( double,::erfc ) + exprtk_define_erfc(long double,::erfcl) + #undef exprtk_define_erfc + #endif + template inline T erfc_impl(T v, real_type_tag) { - #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + #if defined(_MSC_VER) && (_MSC_VER < 1900) return T(1) - erf_impl(v,real_type_tag()); #else - return ::erfc(v); + return erfc_impl(v); #endif } @@ -1196,7 +1216,7 @@ namespace exprtk template inline T asin_impl(const T v, real_type_tag) { return std::asin (v); } template inline T asinh_impl(const T v, real_type_tag) { return std::log(v + std::sqrt((v * v) + T(1))); } template inline T atan_impl(const T v, real_type_tag) { return std::atan (v); } - template inline T atanh_impl(const T v, real_type_tag) { return (std::log(T(1) + v) - log(T(1) - v)) / T(2); } + template inline T atanh_impl(const T v, real_type_tag) { return (std::log(T(1) + v) - std::log(T(1) - v)) / T(2); } template inline T ceil_impl(const T v, real_type_tag) { return std::ceil (v); } template inline T cos_impl(const T v, real_type_tag) { return std::cos (v); } template inline T cosh_impl(const T v, real_type_tag) { return std::cosh (v); } @@ -1269,7 +1289,7 @@ namespace exprtk template struct numeric_info { enum { length = 0, size = 32, bound_length = 0, min_exp = 0, max_exp = 0 }; }; - template<> struct numeric_info { enum { length = 10, size = 16, bound_length = 9}; }; + template<> struct numeric_info { enum { length = 10, size = 16, bound_length = 9}; }; template<> struct numeric_info { enum { min_exp = -38, max_exp = +38}; }; template<> struct numeric_info { enum { min_exp = -308, max_exp = +308}; }; template<> struct numeric_info { enum { min_exp = -308, max_exp = +308}; }; @@ -9223,8 +9243,8 @@ namespace exprtk }; template - class vecarith_vecvec_node : public binary_node , - public vector_interface + class vec_binop_vecvec_node : public binary_node , + public vector_interface { public: @@ -9232,9 +9252,9 @@ namespace exprtk typedef vector_node* vector_node_ptr; typedef vector_holder* vector_holder_ptr; - vecarith_vecvec_node(const operator_type& opr, - expression_ptr branch0, - expression_ptr branch1) + vec_binop_vecvec_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) : binary_node(opr,branch0,branch1), vec0_node_ptr_(0), vec1_node_ptr_(0), @@ -9286,7 +9306,7 @@ namespace exprtk } } - ~vecarith_vecvec_node() + ~vec_binop_vecvec_node() { delete[] data_; delete temp_; @@ -9388,8 +9408,8 @@ namespace exprtk }; template - class vecarith_vecval_node : public binary_node , - public vector_interface + class vec_binop_vecval_node : public binary_node , + public vector_interface { public: @@ -9397,9 +9417,9 @@ namespace exprtk typedef vector_node* vector_node_ptr; typedef vector_holder* vector_holder_ptr; - vecarith_vecval_node(const operator_type& opr, - expression_ptr branch0, - expression_ptr branch1) + vec_binop_vecval_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) : binary_node(opr,branch0,branch1), vec0_node_ptr_(0), vec_size_ (0), @@ -9432,7 +9452,7 @@ namespace exprtk } } - ~vecarith_vecval_node() + ~vec_binop_vecval_node() { delete[] data_; delete temp_; @@ -9530,8 +9550,8 @@ namespace exprtk }; template - class vecarith_valvec_node : public binary_node , - public vector_interface + class vec_binop_valvec_node : public binary_node , + public vector_interface { public: @@ -9539,9 +9559,9 @@ namespace exprtk typedef vector_node* vector_node_ptr; typedef vector_holder* vector_holder_ptr; - vecarith_valvec_node(const operator_type& opr, - expression_ptr branch0, - expression_ptr branch1) + vec_binop_valvec_node(const operator_type& opr, + expression_ptr branch0, + expression_ptr branch1) : binary_node(opr,branch0,branch1), vec1_node_ptr_(0), vec_size_ (0), @@ -9574,7 +9594,7 @@ namespace exprtk } } - ~vecarith_valvec_node() + ~vec_binop_valvec_node() { delete[] data_; delete temp_; @@ -21646,7 +21666,7 @@ namespace exprtk { invalid_state_ = false; - const std::string err_param_seq = s.substr(start,end - start); + const std::string err_param_seq = s.substr(start,end - start); parser_. set_error( @@ -21667,7 +21687,7 @@ namespace exprtk param_seq_list_ = param_seq_list; else { - const std::string err_param_seq = s.substr(start,s.size() - start); + const std::string err_param_seq = s.substr(start,s.size() - start); parser_. set_error( @@ -24282,7 +24302,7 @@ namespace exprtk ); } - inline bool is_vector_eqineq_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) + inline bool is_vector_eqineq_logic_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) { if (!is_ivector_node(branch[0]) && !is_ivector_node(branch[1])) return false; @@ -24294,7 +24314,13 @@ namespace exprtk (details::e_gte == operation) || (details::e_eq == operation) || (details::e_ne == operation) || - (details::e_equal == operation) + (details::e_equal == operation) || + (details::e_and == operation) || + (details::e_nand == operation) || + (details:: e_or == operation) || + (details:: e_nor == operation) || + (details:: e_xor == operation) || + (details::e_xnor == operation) ); } @@ -24328,8 +24354,8 @@ namespace exprtk return synthesize_swap_expression(branch); else if (is_assignment_operation(operation)) return synthesize_assignment_operation_expression(operation,branch); - else if (is_vector_eqineq_operation(operation,branch)) - return synthesize_veceqineq_operation_expression(operation,branch); + else if (is_vector_eqineq_logic_operation(operation,branch)) + return synthesize_veceqineqlogic_operation_expression(operation,branch); else if (is_vector_arithmetic_operation(operation,branch)) return synthesize_vecarithmetic_operation_expression(operation,branch); else if (is_shortcircuit_expression(operation)) @@ -25778,28 +25804,37 @@ namespace exprtk } } - inline expression_node_ptr synthesize_veceqineq_operation_expression(const details::operator_type& operation, - expression_node_ptr (&branch)[2]) + inline expression_node_ptr synthesize_veceqineqlogic_operation_expression(const details::operator_type& operation, + expression_node_ptr (&branch)[2]) { const bool is_b0_ivec = details::is_ivector_node(branch[0]); const bool is_b1_ivec = details::is_ivector_node(branch[1]); + #define batch_eqineq_logic_case \ + case_stmt(details:: e_lt,details:: lt_op) \ + case_stmt(details:: e_lte,details:: lte_op) \ + case_stmt(details:: e_gt,details:: gt_op) \ + case_stmt(details:: e_gte,details:: gte_op) \ + case_stmt(details:: e_eq,details:: eq_op) \ + case_stmt(details:: e_ne,details:: ne_op) \ + case_stmt(details::e_equal,details::equal_op) \ + case_stmt(details:: e_and, details::and_op) \ + case_stmt(details:: e_nand, details::nand_op) \ + case_stmt(details:: e_or, details:: or_op) \ + case_stmt(details:: e_nor, details:: nor_op) \ + case_stmt(details:: e_xor, details:: xor_op) \ + case_stmt(details:: e_xnor, details::xnor_op) \ + if (is_b0_ivec && is_b1_ivec) { switch (operation) { - #define case_stmt(op0,op1) \ - case op0 : return node_allocator_-> \ - template allocate_rrr > > \ - (operation,branch[0],branch[1]); \ + #define case_stmt(op0,op1) \ + case op0 : return node_allocator_-> \ + template allocate_rrr > > \ + (operation,branch[0],branch[1]); \ - case_stmt(details:: e_lt,details:: lt_op) - case_stmt(details:: e_lte,details:: lte_op) - case_stmt(details:: e_gt,details:: gt_op) - case_stmt(details:: e_gte,details:: gte_op) - case_stmt(details:: e_eq,details:: eq_op) - case_stmt(details:: e_ne,details:: ne_op) - case_stmt(details::e_equal,details::equal_op) + batch_eqineq_logic_case #undef case_stmt default : return error_node(); } @@ -25808,18 +25843,12 @@ namespace exprtk { switch (operation) { - #define case_stmt(op0,op1) \ - case op0 : return node_allocator_-> \ - template allocate_rrr > > \ - (operation,branch[0],branch[1]); \ + #define case_stmt(op0,op1) \ + case op0 : return node_allocator_-> \ + template allocate_rrr > > \ + (operation,branch[0],branch[1]); \ - case_stmt(details:: e_lt,details:: lt_op) - case_stmt(details:: e_lte,details:: lte_op) - case_stmt(details:: e_gt,details:: gt_op) - case_stmt(details:: e_gte,details:: gte_op) - case_stmt(details:: e_eq,details:: eq_op) - case_stmt(details:: e_ne,details:: ne_op) - case_stmt(details::e_equal,details::equal_op) + batch_eqineq_logic_case #undef case_stmt default : return error_node(); } @@ -25828,24 +25857,20 @@ namespace exprtk { switch (operation) { - #define case_stmt(op0,op1) \ - case op0 : return node_allocator_-> \ - template allocate_rrr > > \ - (operation,branch[0],branch[1]); \ + #define case_stmt(op0,op1) \ + case op0 : return node_allocator_-> \ + template allocate_rrr > > \ + (operation,branch[0],branch[1]); \ - case_stmt(details:: e_lt,details:: lt_op) - case_stmt(details:: e_lte,details:: lte_op) - case_stmt(details:: e_gt,details:: gt_op) - case_stmt(details:: e_gte,details:: gte_op) - case_stmt(details:: e_eq,details:: eq_op) - case_stmt(details:: e_ne,details:: ne_op) - case_stmt(details::e_equal,details::equal_op) + batch_eqineq_logic_case #undef case_stmt default : return error_node(); } } else return error_node(); + + #undef batch_eqineq_logic_case } inline expression_node_ptr synthesize_vecarithmetic_operation_expression(const details::operator_type& operation, @@ -25865,10 +25890,10 @@ namespace exprtk { switch (operation) { - #define case_stmt(op0,op1) \ - case op0 : return node_allocator_-> \ - template allocate_rrr > > \ - (operation,branch[0],branch[1]); \ + #define case_stmt(op0,op1) \ + case op0 : return node_allocator_-> \ + template allocate_rrr > > \ + (operation,branch[0],branch[1]); \ vector_ops case_stmt(details::e_pow,details:: pow_op) @@ -25880,10 +25905,10 @@ namespace exprtk { switch (operation) { - #define case_stmt(op0,op1) \ - case op0 : return node_allocator_-> \ - template allocate_rrr > > \ - (operation,branch[0],branch[1]); \ + #define case_stmt(op0,op1) \ + case op0 : return node_allocator_-> \ + template allocate_rrr > > \ + (operation,branch[0],branch[1]); \ vector_ops case_stmt(details::e_pow,details:: pow_op) @@ -25895,10 +25920,10 @@ namespace exprtk { switch (operation) { - #define case_stmt(op0,op1) \ - case op0 : return node_allocator_-> \ - template allocate_rrr > > \ - (operation,branch[0],branch[1]); \ + #define case_stmt(op0,op1) \ + case op0 : return node_allocator_-> \ + template allocate_rrr > > \ + (operation,branch[0],branch[1]); \ vector_ops #undef case_stmt diff --git a/exprtk_benchmark.cpp b/exprtk_benchmark.cpp index 26e5e3d..e64c395 100644 --- a/exprtk_benchmark.cpp +++ b/exprtk_benchmark.cpp @@ -183,7 +183,7 @@ bool run_parse_benchmark(exprtk::symbol_table& symbol_table) return true; } -const double pi = 3.14159265358979323846; +const double pi = 3.141592653589793238462643383279502; template struct native diff --git a/exprtk_simple_example_02.cpp b/exprtk_simple_example_02.cpp index 3d707ab..d8859d9 100644 --- a/exprtk_simple_example_02.cpp +++ b/exprtk_simple_example_02.cpp @@ -37,7 +37,7 @@ void square_wave() " (1/21)*sin(42*pi*f*t)+(1/23)*sin(46*pi*f*t)+" " (1/25)*sin(50*pi*f*t)+(1/27)*sin(54*pi*f*t))"; - static const T pi = T(3.14159265358979323846); + static const T pi = T(3.141592653589793238462643383279502); T f = pi / T(10); T t = T(0); diff --git a/exprtk_simple_example_11.cpp b/exprtk_simple_example_11.cpp index 4852155..3ecb6eb 100644 --- a/exprtk_simple_example_11.cpp +++ b/exprtk_simple_example_11.cpp @@ -36,7 +36,7 @@ void square_wave2() " }; " " r *= a * (4 / pi); "; - static const T pi = T(3.14159265358979323846); + static const T pi = T(3.141592653589793238462643383279502); T f = pi / T(10); T t = T(0); diff --git a/exprtk_simple_example_13.cpp b/exprtk_simple_example_13.cpp index 30e2372..36ed5fd 100644 --- a/exprtk_simple_example_13.cpp +++ b/exprtk_simple_example_13.cpp @@ -61,7 +61,7 @@ void savitzky_golay_filter() std::vector v_in; std::vector v_out; - const T pi = T(3.141592653589793238462); + const T pi = T(3.141592653589793238462643383279502); srand(static_cast(time(0))); diff --git a/exprtk_simple_example_17.cpp b/exprtk_simple_example_17.cpp index 9534bbc..997482a 100644 --- a/exprtk_simple_example_17.cpp +++ b/exprtk_simple_example_17.cpp @@ -33,7 +33,8 @@ struct rnd_01 : public exprtk::ifunction inline T operator()() { // Note: Do not use this in production - return T(::rand() / T(RAND_MAX + 1)); + // Result is in the interval [0,1) + return T(::rand() / T(RAND_MAX + 1.0)); } }; @@ -61,7 +62,7 @@ void monte_carlo_pi() const T approximate_pi = expression.value(); - const T real_pi = T(3.141592653589793238462); + const T real_pi = T(3.141592653589793238462643383279502); // or close enough... printf("pi ~ %20.17f\terror: %20.17f\n", approximate_pi, diff --git a/exprtk_test.cpp b/exprtk_test.cpp index 66d4779..8878f5a 100644 --- a/exprtk_test.cpp +++ b/exprtk_test.cpp @@ -29,7 +29,12 @@ #include "exprtk.hpp" +#ifdef exprtk_test_float32_type +typedef float numeric_type; +#else typedef double numeric_type; +#endif + typedef std::pair test_t; static const test_t global_test_list[] = @@ -2750,7 +2755,7 @@ inline bool run_test04() } } - const T pi = T(3.14159265358979323846); + const T pi = T(3.141592653589793238462643383279502); const T increment = T(0.0001); while ((x <= T(+1000)) && (y <= T(+1000))) @@ -2815,7 +2820,7 @@ inline bool run_test05() expression_list.push_back(e); } - const T pi = T(3.14159265358979323846); + const T pi = T(3.141592653589793238462643383279502); const T increment = T(0.001); while ((x <= T(+1000)) && (y <= T(+1000))) @@ -2878,7 +2883,7 @@ inline bool run_test06() T total_area1 = exprtk::integrate(expression,x,T(-1),T(1)); T total_area2 = exprtk::integrate(expression,"x",T(-1),T(1)); - const T pi = T(3.14159265358979323846); + const T pi = T(3.141592653589793238462643383279502); if (not_equal(total_area1,total_area2,T(0.000001))) { @@ -3196,12 +3201,16 @@ struct myfunc : public exprtk::ifunction } }; -double foo1(double v0) { return v0; } -double foo2(double v0, double v1) { return v0 + v1; } -double foo3(double v0, double v1, double v2) { return v0 + v1 + v2; } -double foo4(double v0, double v1, double v2, double v3) { return v0 + v1 + v2 + v3; } -double foo5(double v0, double v1, double v2, double v3, double v4) { return v0 + v1 + v2 + v3 + v4; } -double foo6(double v0, double v1, double v2, double v3, double v4, double v5) { return v0 + v1 + v2 + v3 + v4 + v5; } +#define define_free_functions(Type) \ +Type foo1(Type v0) { return v0; } \ +Type foo2(Type v0, Type v1) { return v0 + v1; } \ +Type foo3(Type v0, Type v1, Type v2) { return v0 + v1 + v2; } \ +Type foo4(Type v0, Type v1, Type v2, Type v3) { return v0 + v1 + v2 + v3; } \ +Type foo5(Type v0, Type v1, Type v2, Type v3, Type v4) { return v0 + v1 + v2 + v3 + v4; } \ +Type foo6(Type v0, Type v1, Type v2, Type v3, Type v4, Type v5) { return v0 + v1 + v2 + v3 + v4 + v5; } \ + +define_free_functions(numeric_type) +#undef define_free_functions template inline bool run_test09() @@ -3267,7 +3276,7 @@ inline bool run_test09() } } - const T pi = T(3.141592653589793238462); + const T pi = T(3.141592653589793238462643383279502); T result = expression.value(); diff --git a/readme.txt b/readme.txt index 2ed1891..3a9d955 100644 --- a/readme.txt +++ b/readme.txt @@ -1269,12 +1269,13 @@ with vectors: (b) Exponentiation: vector ^ scalar (c) Assignment: :=, +=, -=, *=, /=, %=, <=> (d) Inequalities: <, <=, >, >=, ==, =, equal - (e) Unary operations: + (e) Boolean logic: and, nand, nor, or, xnor, xor + (f) Unary operations: abs, acos, acosh, asin, asinh, atan, atanh, ceil, cos, cosh, cot, csc, deg2grad, deg2rad, erf, erfc, exp, expm1, floor, frac, grad2deg, log, log10, log1p, log2, rad2deg, round, sec, sgn, sin, sinc, sinh, sqrt, swap, tan, tanh, trunc - (f) Aggregate and Reduce operations: + (g) Aggregate and Reduce operations: avg, max, min, mul, sum Note: When one of the above described operations is being performed @@ -1315,6 +1316,7 @@ not a vector but rather a single value. avg(3x + 1) == 7 min(1 / x) == (1 / 3) max(x / 2) == (3 / 2) + sum(x > 0 and x < 5) == x[] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2487,6 +2489,16 @@ in the event of a failed compilation. } +Assuming the following expression '2 + (3 / log(1 + x))' which uses a +variable named 'x' that has not been registered with the appropriate +symbol_table instance and is not a locally defined variable, once +compiled the above denoted post compilation error handling code shall +produce the following output: + + Error: ERR184 - Undefined symbol: 'x' + Err No.:00 Pos:17 Type:[Syntax] Msg: ERR184 - Undefined symbol: 'x' + + For expressions comprised of multiple lines, the error position provided in the parser_error object can be converted into a pair of line and column numbers by invoking the 'update_error' function as is @@ -2730,9 +2742,40 @@ into account when using ExprTk: (x + y) / (x - y); } + (30) For performance considerations, one should assume the actions + of expression, symbol table and parser instance instantiation + and destruction, and the expression compilation process itself + to be of high latency. Hence none of them should be part of any + performance critical code paths, and should instead occur + entirely either before or after such code paths. + + (31) Before jumping in and using ExprTk, do take the time to peruse + the documentation and all of the examples, both in the main and + the extras distributions. Having an informed general view of + what can and can't be done, and how something should be done + with ExprTk, will likely result in a far more productive and + enjoyable programming experience. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [21 - SIMPLE EXPRTK EXAMPLE] +The following is a simple yet complete example demonstrating typical +usage of the ExprTk Library. The example instantiates a symbol table +object, adding to it three variables named x, y and z, and a custom +user defined function, that accepts only two parameters, named myfunc. +The example then proceeds to instantiate an expression object and +register to it the symbol table instance. + +A parser is then instantiated, and the string representation of the +expression and the expression object are passed to the parser's +compile method for compilation. If an error occurred during +compilation, the compile method will return false, leading to a series +of error diagnostics being printed to stdout. Otherwise the newly +compiled expression is evaluated by invoking the expression object's +value method, and subsequently printing the result of the computation +to stdout. + + --- snip --- #include #include