diff options
Diffstat (limited to 'boost/spirit/home/qi/numeric/detail/real_impl.hpp')
-rw-r--r-- | boost/spirit/home/qi/numeric/detail/real_impl.hpp | 125 |
1 files changed, 92 insertions, 33 deletions
diff --git a/boost/spirit/home/qi/numeric/detail/real_impl.hpp b/boost/spirit/home/qi/numeric/detail/real_impl.hpp index a4bd8789b6..cf92712a24 100644 --- a/boost/spirit/home/qi/numeric/detail/real_impl.hpp +++ b/boost/spirit/home/qi/numeric/detail/real_impl.hpp @@ -20,6 +20,7 @@ #include <boost/spirit/home/qi/detail/attributes.hpp> #include <boost/spirit/home/support/detail/pow10.hpp> #include <boost/spirit/home/support/detail/sign.hpp> +#include <boost/integer.hpp> #include <boost/assert.hpp> #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) @@ -31,49 +32,89 @@ namespace boost { namespace spirit { namespace traits { using spirit::traits::pow10; + + namespace detail + { + template <typename T, typename AccT> + void compensate_roundoff(T& n, AccT acc_n, mpl::true_) + { + // at the lowest extremes, we compensate for floating point + // roundoff errors by doing imprecise computation using T + int const comp = 10; + n = T((acc_n / comp) * comp); + n += T(acc_n % comp); + } + + template <typename T, typename AccT> + void compensate_roundoff(T& n, AccT acc_n, mpl::false_) + { + // no need to compensate + n = acc_n; + } + + template <typename T, typename AccT> + void compensate_roundoff(T& n, AccT acc_n) + { + compensate_roundoff(n, acc_n, is_integral<AccT>()); + } + } - template <typename T> - inline void - scale(int exp, T& n) + template <typename T, typename AccT> + inline bool + scale(int exp, T& n, AccT acc_n) { if (exp >= 0) { - // $$$ Why is this failing for boost.math.concepts ? $$$ - //~ int nn = std::numeric_limits<T>::max_exponent10; - //~ BOOST_ASSERT(exp <= std::numeric_limits<T>::max_exponent10); - n *= pow10<T>(exp); + std::size_t max_exp = std::numeric_limits<T>::max_exponent10; + + // return false if exp exceeds the max_exp + // do this check only for primitive types! + if (is_floating_point<T>() && exp > max_exp) + return false; + n = acc_n * pow10<T>(exp); } else { if (exp < std::numeric_limits<T>::min_exponent10) { - n /= pow10<T>(-std::numeric_limits<T>::min_exponent10); - n /= pow10<T>(-exp + std::numeric_limits<T>::min_exponent10); + int min_exp = std::numeric_limits<T>::min_exponent10; + detail::compensate_roundoff(n, acc_n); + n /= pow10<T>(-min_exp); + + // return false if (-exp + min_exp) exceeds the -min_exp + // do this check only for primitive types! + if (is_floating_point<T>() && (-exp + min_exp) > -min_exp) + return false; + + n /= pow10<T>(-exp + min_exp); } else { - n /= pow10<T>(-exp); + n = T(acc_n) / pow10<T>(-exp); } } + return true; } - inline void - scale(int /*exp*/, unused_type /*n*/) + inline bool + scale(int /*exp*/, unused_type /*n*/, unused_type /*acc_n*/) { // no-op for unused_type + return true; } - template <typename T> - inline void - scale(int exp, int frac, T& n) + template <typename T, typename AccT> + inline bool + scale(int exp, int frac, T& n, AccT acc_n) { - scale(exp - frac, n); + return scale(exp - frac, n, acc_n); } - inline void + inline bool scale(int /*exp*/, int /*frac*/, unused_type /*n*/) { // no-op for unused_type + return true; } inline float @@ -121,6 +162,17 @@ namespace boost { namespace spirit { namespace traits // no-op for unused_type return false; } + + template <typename T> + struct real_accumulator : mpl::identity<T> {}; + + template <> + struct real_accumulator<float> + : mpl::identity<uint_t<(sizeof(float)*CHAR_BIT)>::least> {}; + + template <> + struct real_accumulator<double> + : mpl::identity<uint_t<(sizeof(double)*CHAR_BIT)>::least> {}; }}} namespace boost { namespace spirit { namespace qi { namespace detail @@ -142,8 +194,10 @@ namespace boost { namespace spirit { namespace qi { namespace detail bool neg = p.parse_sign(first, last); // Now attempt to parse an integer - T n = 0; - bool got_a_number = p.parse_n(first, last, n); + T n; + + typename traits::real_accumulator<T>::type acc_n = 0; + bool got_a_number = p.parse_n(first, last, acc_n); // If we did not get a number it might be a NaN, Inf or a leading // dot. @@ -168,6 +222,7 @@ namespace boost { namespace spirit { namespace qi { namespace detail } bool e_hit = false; + Iterator e_pos; int frac_digits = 0; // Try to parse the dot ('.' decimal point) @@ -176,14 +231,8 @@ namespace boost { namespace spirit { namespace qi { namespace detail // We got the decimal point. Now we will try to parse // the fraction if it is there. If not, it defaults // to zero (0) only if we already got a number. - Iterator savef = first; - if (p.parse_frac_n(first, last, n)) + if (p.parse_frac_n(first, last, acc_n, frac_digits)) { - // Optimization note: don't compute frac_digits if T is - // an unused_type. This should be optimized away by the compiler. - if (!is_same<T, unused_type>::value) - frac_digits = - static_cast<int>(std::distance(savef, first)); } else if (!got_a_number || !p.allow_trailing_dot) { @@ -195,6 +244,7 @@ namespace boost { namespace spirit { namespace qi { namespace detail } // Now, let's see if we can parse the exponent prefix + e_pos = first; e_hit = p.parse_exp(first, last); } else @@ -208,6 +258,7 @@ namespace boost { namespace spirit { namespace qi { namespace detail // If we must expect a dot and we didn't see an exponent // prefix, return no-match. + e_pos = first; e_hit = p.parse_exp(first, last); if (p.expect_dot && !e_hit) { @@ -219,27 +270,29 @@ namespace boost { namespace spirit { namespace qi { namespace detail if (e_hit) { // We got the exponent prefix. Now we will try to parse the - // actual exponent. It is an error if it is not there. + // actual exponent. int exp = 0; if (p.parse_exp_n(first, last, exp)) { // Got the exponent value. Scale the number by // exp-frac_digits. - traits::scale(exp, frac_digits, n); + if (!traits::scale(exp, frac_digits, n, acc_n)) + return false; } else { - // Oops, no exponent, return no-match. - first = save; - return false; + // If there is no number, disregard the exponent altogether. + // by resetting 'first' prior to the exponent prefix (e|E) + first = e_pos; + n = acc_n; } } else if (frac_digits) { // No exponent found. Scale the number by -frac_digits. - traits::scale(-frac_digits, n); + traits::scale(-frac_digits, n, acc_n); } - else if (traits::is_equal_to_one(n)) + else if (traits::is_equal_to_one(acc_n)) { // There is a chance of having to parse one of the 1.0#... // styles some implementations use for representing NaN or Inf. @@ -252,6 +305,12 @@ namespace boost { namespace spirit { namespace qi { namespace detail traits::assign_to(traits::negate(neg, n), attr); return true; // got a NaN or Inf, return immediately } + + n = acc_n; + } + else + { + n = acc_n; } // If we got a negative sign, negate the number |