// Spirit_Price.cpp // #include #include #include #include #include #include #include #include #include #include #include #include "high_resolution_timer.hpp" #define DO_COMPILE_ORIGINAL_CUSTOM 1 #define DO_COMPILE_XPRESSIVE 1 #define DO_COMPILE_SPIRIT_QUICK 1 #define DO_COMPILE_SPIRIT_QUICK_NEW 1 #define DO_COMPILE_SPIRIT_GRAMMAR 1 const size_t iterationsToDo = 10000000; ////////////////////////////////////////////////////////////////////////// // Original code to compare with ////////////////////////////////////////////////////////////////////////// #if 0 template T foo::extract(char const * & _input, char const * const _description, std::string const & _value) { T result; if (!bar::to_number(result, _input, &_input, 10)) { raise_extract_failed(_description, _value); } return result; } inline boost::long_long_type foo::round(const double _value) { return static_cast(std::floor(_value + 0.5)); } boost::long_long_type foo::parse(std::string const & _value) { if (_value.empty()) { return 0; } size_t offset(_value.find_first_not_of("-+0123456789eE ./")); const bool invalid(std::string::npos != offset); if (invalid) { raise_parsing_failed(_value); } const char * const input(_value.c_str()); const char * const last(input + _value.length()); const char * end(input); boost::long_long_type result; (void)bar::to_number(result, end, &end, 10); const bool floating_point('.' == *end); if (floating_point) { end = input; const double value(extract(end, "input", _value)); const bool negative(0.0 > value); double multiple(value * 160000.0); if (negative) { multiple = -multiple; } result = round(multiple); if (negative) { result = -result; } } else // not floating point { result *= 160000; if (end != last) { offset = _value.find_first_of('/', end - input); const bool is_fraction(std::string::npos != offset); if (is_fraction) { const bool negative(0 > result); if (negative) { result = -result; } offset = _value.find_last_of(' ', offset); const bool is_mixed_number(std::string::npos != offset); if (is_mixed_number) { const unsigned numerator( extract(end, "numerator", _value)); end += 1; // skip the slash const unsigned denominator( extract(end, "denominator", _value)); const double fraction((160000.0 * numerator) / denominator); result = round(result + fraction); } else { end += 1; // skip the slash const unsigned denominator( extract(end, "denominator", _value)); const double fraction(result / denominator); result = round(fraction); } if (negative) { result = -result; } } } } return result; } #endif ////////////////////////////////////////////////////////////////////////// // End original code ////////////////////////////////////////////////////////////////////////// namespace core { inline bool to_number( double& result, char const* in, char const** end, int radix) { result = strtod(in, const_cast(end)); return **end == '\0'; } inline bool to_number( long long& result, char const* in, char const** end, int radix) { result = strtoll(in, const_cast(end), radix); return **end == '\0'; } inline bool to_number( unsigned int& result, char const* in, char const** end, int radix) { result = strtoul(in, const_cast(end), radix); return **end == '\0'; } inline bool to_number( int& result, char const* in, char const** end, int radix) { result = strtol(in, const_cast(end), radix); return **end == '\0'; } } ////////////////////////////////////////////////////////////////////////// // Start custom code ////////////////////////////////////////////////////////////////////////// namespace example { struct lcm_generator { lcm_generator() { } template T as() const; }; template <> inline unsigned lcm_generator::as() const { return 160000U; } template <> inline boost::long_long_type lcm_generator::as() const { return 160000; } template <> inline double lcm_generator::as() const { return 160000.0; } boost::long_long_type round(double); lcm_generator const lcm; } // ***************************************************************************** // Design Notes: Adding 0.5 to _value before calling std::floor(), which // rounds down, causes rounding to the nearest whole number. This only works // for positive values, however. // ----------------------------------------------------------------------------- inline boost::long_long_type example::round(double const _value) { return static_cast(std::floor(_value + 0.5)); } #if DO_COMPILE_ORIGINAL_CUSTOM namespace example { namespace custom { template T extract(char const * & _input, char const * _description, std::string const & _value); boost::long_long_type parse(std::string const & _value); boost::long_long_type parse_fraction(char const *& _end, boost::long_long_type _result, char const * _denominator, bool _is_mixed_number, std::string const & _value); void raise_extract_failed(char const * _description, std::string const & _value) { throw 0; } void raise_zero_denominator(std::string const &) { throw 0; } } } // ***************************************************************************** // Design Notes: Non-dependent exception throwing code appears in // raise_extract_failed(). // ----------------------------------------------------------------------------- template T example::custom::extract(char const * & _input, char const * const _description, std::string const & _value) { T result; if (!core::to_number(result, _input, &_input, 10)) { raise_extract_failed(_description, _value); } return result; } // ***************************************************************************** // Design Notes: This function parses several number formats: floating // point, whole number, fraction, and mixed number. All formats begin with a // number, so the input is first parsed and converted to boost::long_long_type, base 10. If // a decimal point appears immediately after the input consumed for that // conversion, then reparse from the beginning as floating point rather than // jump through hoops to compute the fractional part. If there is a space // after the initial number, then the initial number is a whole number that may // be followed by a fraction, meaning the input was a mixed number. If there // is a slash after the initial number, then the initial number is the // numerator of a fraction. // // When converting a whole number, it is important to avoid floating point // representational and radix problems. The boost::long_long_type conversion is done with // base 10, thus ignoring any leading zeroes. Conversion to double would work // without specifying the base, but then it is difficult to know whether to // look for a fraction, for a mixed number, and the integer value may not be // representable in a double. // // Negative values, except when the input is a whole number, must be processed // as positive until after the fractional part has been parsed and computed to // simplify accounting for rounding errors in round() and to account correctly // for magnitude. // // When computing a fraction, the product of lcm and numerator is divided by // denominator in order to reduce rounding and representational errors that // would result from first computing the quotient of numerator and denominator. // ----------------------------------------------------------------------------- boost::long_long_type example::custom::parse(std::string const & _value) { assert(std::string::npos != _value.find_first_not_of("-+0123456789eE ./")); if (_value.empty()) { return 0; } char const * const start(_value.c_str()); char const * end(start); boost::long_long_type result; (void)core::to_number(result, end, &end, 10); bool const is_floating_point('.' == *end); if (is_floating_point) { end = start; double value(extract(end, "input", _value) * lcm.as()); bool const is_negative(0 > result); if (is_negative) { value = -value; } result = round(value); if (is_negative) { result = -result; } } else { result *= lcm.as(); char const * const last(start + _value.length()); if (end != last) { size_t const slash(_value.find_first_of('/', end - start)); bool const is_fraction(std::string::npos != slash); if (is_fraction) { char const * const denominator(start + slash + 1); bool const is_mixed_number( std::string::npos != _value.find_last_of(' ', slash)); bool const is_non_negative(0 <= result); if (is_non_negative) { result = parse_fraction(end, result, denominator, is_mixed_number, _value); } else { result = -parse_fraction(end, -result, denominator, is_mixed_number, _value); } } } } assert(end == (start + _value.length()) || std::string::npos == _value.find_first_not_of( " \t\r\n", end - start, 4)); return result; } // ***************************************************************************** // Design Notes: // ----------------------------------------------------------------------------- boost::long_long_type example::custom::parse_fraction(char const *& _end, boost::long_long_type const _result, char const * const _denominator, bool const _is_mixed_number, std::string const & _value) { double value; if (_is_mixed_number) { unsigned const numerator(extract(_end, "numerator", _value)); _end = _denominator; unsigned const denominator( extract(_end, "denominator", _value)); double fraction(numerator * lcm.as() / denominator); value = _result + fraction; } else { _end = _denominator; unsigned const denominator( extract(_end, "denominator", _value)); value = double(_result / denominator); // added double() to prevent the cast warning } boost::long_long_type const result(round(value)); return result; } #endif ////////////////////////////////////////////////////////////////////////// // End custom code ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // Start xpressive code ////////////////////////////////////////////////////////////////////////// #if DO_COMPILE_XPRESSIVE namespace example { namespace xpressive { struct direct_impl { typedef boost::long_long_type result_type; template boost::long_long_type operator ()(Value const & _value) const { //boost::long_long_type result(core::numeric_cast(_value)); ////////////////////////////////////////////////////////////////////////// // Work around for missing core::numeric_cast //boost::long_long_type result(boost::lexical_cast(_value)); boost::long_long_type result; //std::string va(_value); std::string::const_iterator b = _value.first; boost::spirit::qi::parse(b, _value.second, boost::spirit::long_long[boost::phoenix::ref(result)]); result *= lcm.as(); return result; } }; // exception type indicating denominator was zero struct zero_denominator { }; struct to_price_impl { typedef boost::long_long_type result_type; template boost::long_long_type operator ()(Value const & _value) const { boost::long_long_type result(0); if (_value) { //double const value(core::numeric_cast(_value)); //double const value(boost::lexical_cast(_value)); double value; //std::string va(_value); std::string::const_iterator b = _value.first; boost::spirit::qi::parse(b, _value.second, boost::spirit::double_[boost::phoenix::ref(value)]); result = round(lcm.as() * value); } return result; } template boost::long_long_type operator ()(Value const & _numerator, Value const & _denominator) const { //unsigned const numerator(core::numeric_cast(_numerator)); //unsigned const numerator(boost::lexical_cast(_numerator)); unsigned numerator; //std::string nu(_numerator); std::string::const_iterator b = _numerator.first; boost::spirit::qi::parse(b, _numerator.second, boost::spirit::uint_[boost::phoenix::ref(numerator)]); //unsigned const denominator( // core::numeric_cast(_denominator)); // boost::lexical_cast(_denominator)); unsigned denominator; //std::string de(_denominator); b = _denominator.first; boost::spirit::qi::parse(b, _denominator.second, boost::spirit::uint_[boost::phoenix::ref(denominator)]); if (0 == denominator) { throw zero_denominator(); } double const fraction(lcm.as() * numerator / denominator); return round(fraction); } template boost::long_long_type operator ()(Value const & _whole, double const _fraction) const { //int const whole(core::numeric_cast(_whole)); //int const whole(boost::lexical_cast(_whole)); int whole; //std::string wh(_whole); std::string::const_iterator b = _whole.first; boost::spirit::qi::parse(b, _whole.second, boost::spirit::uint_[boost::phoenix::ref(whole)]); double const value(lcm.as() * whole + _fraction); return round(value); } }; //ThreadLocal match_s; boost::xpressive::smatch match_s; // removed ThreadLocal as it does not exist. } } namespace example { namespace xpressive { namespace prices { using namespace boost::xpressive; function::type const direct = {{}}; function::type const to_price = {{}}; placeholder is_negative_; placeholder price_; const BOOST_PROTO_AUTO(zero, as_xpr('0')); const BOOST_PROTO_AUTO(sign, as_xpr('-') [ is_negative_ = true ] | as_xpr('+')); // ignore const BOOST_PROTO_AUTO(fraction, ( *zero >> (s1= +_d) >> *blank >> '/' >> *blank >> *zero >> (s2= +_d) ) [ price_ = to_price(s1, s2) ]); const BOOST_PROTO_AUTO(real, ( *_d >> '.' >> *_d >> !((set= 'E', 'e', 'G', 'g') >> +_d) ) [ price_ = to_price(_) ]); const BOOST_PROTO_AUTO(whole_number, ( *zero >> (s1= +_d) >> +blank >> fraction ) [ price_ = to_price(s1, price_) ]); const BOOST_PROTO_AUTO(integer, ( *zero >> (s1= +_d) ) [ price_ = direct(s1) ]); const sregex price = *blank >> !sign >> (real | whole_number | fraction | integer) >> *space; } } } // ***************************************************************************** // Design Notes: // ----------------------------------------------------------------------------- namespace example { namespace xpressive { boost::long_long_type parse(const std::string & _value) { typedef std::string::const_iterator iterator; using namespace prices; boost::long_long_type result; iterator begin(_value.begin()); iterator end(_value.end()); bool is_negative(false); //boost::xpressive::smatch & match(match_s.getValue()); boost::xpressive::smatch & match(match_s); match.let(price_ = result); match.let(is_negative_ = is_negative); bool matched; try { matched = boost::xpressive::regex_match(begin, end, match, price); } catch (zero_denominator) { //raise_zero_denominator(_value); } if (!matched) { //raise_parse_failure(_value); } if (is_negative) { result = -result; } return result; } } } #endif ////////////////////////////////////////////////////////////////////////// // End xpressive code ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // Start spirit-quick code ////////////////////////////////////////////////////////////////////////// #if DO_COMPILE_SPIRIT_QUICK BOOST_STATIC_ASSERT(sizeof(boost::long_long_type) == 8); boost::long_long_type tmpResult; inline void dotNumber(double a1) { const bool negative(0.0 > a1); if(negative) a1 = -a1; tmpResult = static_cast(std::floor(160000.0*a1 + 0.5)); if(negative) tmpResult = -tmpResult; } template bool parse_price_spirit_quick(Iterator first, Iterator last, boost::long_long_type &c) { using boost::spirit::qi::double_; using boost::spirit::qi::_1; using boost::spirit::qi::_2; using boost::spirit::qi::phrase_parse; using boost::spirit::qi::lexeme; using boost::spirit::qi::lit; using boost::spirit::ascii::space; using boost::spirit::ascii::blank; using boost::phoenix::ref; using boost::spirit::long_long; using boost::long_long_type; bool r = phrase_parse(first, last, // Begin grammar ( (long_long[ref(tmpResult)=(_1*160000)] >> !lit('.') >> -(long_long >> '/' >> long_long)[ref(tmpResult)+=((160000*_1)/_2)]) | double_[&dotNumber] ) // End grammar ,blank); if (!r || first != last) // fail if we did not get a full match return false; c = tmpResult; return r; } #endif ////////////////////////////////////////////////////////////////////////// // End spirit-quick code ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // Start spirit-quick code ////////////////////////////////////////////////////////////////////////// #if DO_COMPILE_SPIRIT_QUICK_NEW BOOST_STATIC_ASSERT(sizeof(boost::long_long_type) == 8); struct dot_number_to_long_long_function_quick_new { boost::long_long_type &res; dot_number_to_long_long_function_quick_new(boost::long_long_type &_res):res(_res) {} template void operator()(double val, Inst, bool &pass) const { const bool negative(0.0 > val); if(negative) val = -val; res = static_cast(std::floor(160000.0*val + 0.5)); if(negative) res = -res; } }; template bool parse_price_spirit_quick_new(Iterator first, Iterator last, boost::long_long_type &c) { using boost::spirit::qi::double_; using boost::spirit::qi::_1; using boost::spirit::qi::_2; using boost::spirit::qi::phrase_parse; using boost::spirit::qi::lexeme; using boost::spirit::qi::lit; using boost::spirit::ascii::space; using boost::spirit::ascii::blank; using boost::phoenix::ref; using boost::spirit::long_long; boost::long_long_type res; bool r = phrase_parse(first, last, // Begin grammar ( (long_long[ref(res)=(_1*160000)] >> !lit('.') >> -(long_long >> '/' >> long_long)[ref(res)+=((160000*_1)/_2)]) | double_[dot_number_to_long_long_function_quick_new(res)] ) // End grammar ,blank); if (!r || first != last) // fail if we did not get a full match return false; c = res; return r; } #endif ////////////////////////////////////////////////////////////////////////// // End spirit-quick code ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // Start spirit-grammar code ////////////////////////////////////////////////////////////////////////// #if DO_COMPILE_SPIRIT_GRAMMAR BOOST_STATIC_ASSERT(sizeof(boost::long_long_type) == 8); struct dot_number_to_long_long_function { //template //struct result{ template void operator()(double val, Inst inst, bool&) const { const bool negative(0.0 > val); if(negative) val = -val; inst.attributes.car = static_cast(std::floor(160000.0*val + 0.5)); if(negative) inst.attributes.car = -inst.attributes.car; } }; template struct price_grammar : boost::spirit::qi::grammar { price_grammar() : price_grammar::base_type(price) { using boost::spirit::qi::long_long; using boost::spirit::qi::lit; using boost::spirit::qi::double_; using boost::spirit::qi::_val; using boost::spirit::qi::_1; using boost::spirit::qi::_2; price %= int_whole_part | dotted_part ; int_whole_part = long_long [_val = (_1*160000)] >> !lit('.') >> -whole_part [_val += _1] ; whole_part = (long_long >> lit('/') >> long_long) [_val = ((160000*_1)/_2)] ; dotted_part = double_[dot_number_to_long_long_function()] ; } boost::spirit::qi::rule price; boost::spirit::qi::rule int_whole_part; boost::spirit::qi::rule whole_part; boost::spirit::qi::rule dotted_part; }; template struct parse_price_spirit_grammar { price_grammar price_gram; bool operator()(Iterator first, Iterator last, boost::long_long_type &c) { boost::long_long_type res; bool r = boost::spirit::qi::phrase_parse(first, last, price_gram, boost::spirit::ascii::blank, res); if (!r || first != last) // fail if we did not get a full match return false; c = res; return r; } }; // template // bool parse_price_spirit_grammar(Iterator first, Iterator last, boost::long_long_type &c) // { // using boost::long_long_type; // // price_grammar price_gram; // // boost::long_long_type res; // // bool r = boost::spirit::qi::phrase_parse(first, last, price_gram, boost::spirit::ascii::blank, res); // // if (!r || first != last) // fail if we did not get a full match // return false; // // c = res; // // return r; // } #endif ////////////////////////////////////////////////////////////////////////// // End spirit-grammar code ////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { char const *s1 = "425/500", *e1 = s1 + std::strlen(s1); std::cout << "Loop count: " << iterationsToDo << std::endl; std::cout << "Parsing: " << s1 << std::endl; #if DO_COMPILE_ORIGINAL_CUSTOM { boost::long_long_type result; size_t counter = iterationsToDo; util::high_resolution_timer t; while(counter--) result=example::custom::parse(s1); std::cout << "original-custom: " << t.elapsed() << std::endl; //std::cout << "cannot attempt the original code as it is missing a core::to_number function." << std::endl; } #endif #if DO_COMPILE_XPRESSIVE { boost::long_long_type result; size_t counter = iterationsToDo; util::high_resolution_timer t; while(counter--) result=example::xpressive::parse(s1); std::cout << "xpressive: " << t.elapsed() << std::endl; } #endif #if DO_COMPILE_SPIRIT_QUICK { boost::long_long_type result; size_t counter = iterationsToDo; util::high_resolution_timer t; while(counter--) parse_price_spirit_quick(s1, e1, result); std::cout << "spirit-quick(static): " << t.elapsed() << std::endl; } #endif #if DO_COMPILE_SPIRIT_QUICK_NEW { boost::long_long_type result; size_t counter = iterationsToDo; util::high_resolution_timer t; while(counter--) parse_price_spirit_quick_new(s1, e1, result); std::cout << "spirit-quick_new(threadsafe): " << t.elapsed() << std::endl; } #endif #if DO_COMPILE_SPIRIT_GRAMMAR { boost::long_long_type result; size_t counter = iterationsToDo; parse_price_spirit_grammar parser; util::high_resolution_timer t; while(counter--) parser(s1, e1, result); std::cout << "spirit-grammar(threadsafe/reusable): " << t.elapsed() << std::endl; } #endif return 0; }