summaryrefslogtreecommitdiff
path: root/boost/spirit/home/qi/numeric/detail/real_impl.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/spirit/home/qi/numeric/detail/real_impl.hpp')
-rw-r--r--boost/spirit/home/qi/numeric/detail/real_impl.hpp125
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