• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*=============================================================================
2     Copyright (c) 2001-2019 Joel de Guzman
3     Copyright (c) 2001-2011 Hartmut Kaiser
4     http://spirit.sourceforge.net/
5 
6     Distributed under the Boost Software License, Version 1.0. (See accompanying
7     file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 =============================================================================*/
9 #ifndef BOOST_SPIRIT_QI_NUMERIC_DETAIL_REAL_IMPL_HPP
10 #define BOOST_SPIRIT_QI_NUMERIC_DETAIL_REAL_IMPL_HPP
11 
12 #if defined(_MSC_VER)
13 #pragma once
14 #endif
15 
16 #include <cmath>
17 #include <boost/limits.hpp>
18 #include <boost/type_traits/is_same.hpp>
19 #include <boost/spirit/home/support/unused.hpp>
20 #include <boost/spirit/home/qi/detail/attributes.hpp>
21 #include <boost/spirit/home/support/detail/pow10.hpp>
22 #include <boost/spirit/home/support/detail/sign.hpp>
23 #include <boost/integer.hpp>
24 #include <boost/assert.hpp>
25 
26 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
27 # pragma warning(push)
28 # pragma warning(disable: 4100)   // 'p': unreferenced formal parameter
29 # pragma warning(disable: 4127)   // conditional expression is constant
30 #endif
31 
32 namespace boost { namespace spirit { namespace traits
33 {
34     using spirit::traits::pow10;
35 
36     namespace detail
37     {
38         template <typename T, typename AccT>
compensate_roundoff(T & n,AccT acc_n,mpl::true_)39         void compensate_roundoff(T& n, AccT acc_n, mpl::true_)
40         {
41             // at the lowest extremes, we compensate for floating point
42             // roundoff errors by doing imprecise computation using T
43             int const comp = 10;
44             n = T((acc_n / comp) * comp);
45             n += T(acc_n % comp);
46         }
47 
48         template <typename T, typename AccT>
compensate_roundoff(T & n,AccT acc_n,mpl::false_)49         void compensate_roundoff(T& n, AccT acc_n, mpl::false_)
50         {
51             // no need to compensate
52             n = acc_n;
53         }
54 
55         template <typename T, typename AccT>
compensate_roundoff(T & n,AccT acc_n)56         void compensate_roundoff(T& n, AccT acc_n)
57         {
58             compensate_roundoff(n, acc_n, is_integral<AccT>());
59         }
60     }
61 
62     template <typename T, typename AccT>
63     inline bool
scale(int exp,T & n,AccT acc_n)64     scale(int exp, T& n, AccT acc_n)
65     {
66         if (exp >= 0)
67         {
68             int const max_exp = std::numeric_limits<T>::max_exponent10;
69 
70             // return false if exp exceeds the max_exp
71             // do this check only for primitive types!
72             if (is_floating_point<T>() && (exp > max_exp))
73                 return false;
74             n = acc_n * pow10<T>(exp);
75         }
76         else
77         {
78             if (exp < std::numeric_limits<T>::min_exponent10)
79             {
80                 int const min_exp = std::numeric_limits<T>::min_exponent10;
81                 detail::compensate_roundoff(n, acc_n);
82                 n /= pow10<T>(-min_exp);
83 
84                 // return false if exp still exceeds the min_exp
85                 // do this check only for primitive types!
86                 exp += -min_exp;
87                 if (is_floating_point<T>() && exp < min_exp)
88                     return false;
89 
90                 n /= pow10<T>(-exp);
91             }
92             else
93             {
94                 n = T(acc_n) / pow10<T>(-exp);
95             }
96         }
97         return true;
98     }
99 
100     inline bool
scale(int,unused_type,unused_type)101     scale(int /*exp*/, unused_type /*n*/, unused_type /*acc_n*/)
102     {
103         // no-op for unused_type
104         return true;
105     }
106 
107     template <typename T, typename AccT>
108     inline bool
scale(int exp,int frac,T & n,AccT acc_n)109     scale(int exp, int frac, T& n, AccT acc_n)
110     {
111         return scale(exp - frac, n, acc_n);
112     }
113 
114     inline bool
scale(int,int,unused_type)115     scale(int /*exp*/, int /*frac*/, unused_type /*n*/)
116     {
117         // no-op for unused_type
118         return true;
119     }
120 
121     inline float
negate(bool neg,float n)122     negate(bool neg, float n)
123     {
124         return neg ? spirit::detail::changesign(n) : n;
125     }
126 
127     inline double
negate(bool neg,double n)128     negate(bool neg, double n)
129     {
130         return neg ? spirit::detail::changesign(n) : n;
131     }
132 
133     inline long double
negate(bool neg,long double n)134     negate(bool neg, long double n)
135     {
136         return neg ? spirit::detail::changesign(n) : n;
137     }
138 
139     template <typename T>
140     inline T
negate(bool neg,T const & n)141     negate(bool neg, T const& n)
142     {
143         return neg ? -n : n;
144     }
145 
146     inline unused_type
negate(bool,unused_type n)147     negate(bool /*neg*/, unused_type n)
148     {
149         // no-op for unused_type
150         return n;
151     }
152 
153     template <typename T>
154     struct real_accumulator : mpl::identity<T> {};
155 
156     template <>
157     struct real_accumulator<float>
158         : mpl::identity<uint_t<(sizeof(float)*CHAR_BIT)>::least> {};
159 
160     template <>
161     struct real_accumulator<double>
162         : mpl::identity<uint_t<(sizeof(double)*CHAR_BIT)>::least> {};
163 }}}
164 
165 namespace boost { namespace spirit { namespace qi  { namespace detail
166 {
167     BOOST_MPL_HAS_XXX_TRAIT_DEF(version)
168 
169     template <typename T, typename RealPolicies>
170     struct real_impl
171     {
172         template <typename Iterator>
173         static std::size_t
ignore_excess_digitsboost::spirit::qi::detail::real_impl174         ignore_excess_digits(Iterator& /* first */, Iterator const& /* last */, mpl::false_)
175         {
176             return 0;
177         }
178 
179         template <typename Iterator>
180         static std::size_t
ignore_excess_digitsboost::spirit::qi::detail::real_impl181         ignore_excess_digits(Iterator& first, Iterator const& last, mpl::true_)
182         {
183             return RealPolicies::ignore_excess_digits(first, last);
184         }
185 
186         template <typename Iterator>
187         static std::size_t
ignore_excess_digitsboost::spirit::qi::detail::real_impl188         ignore_excess_digits(Iterator& first, Iterator const& last)
189         {
190             typedef mpl::bool_<has_version<RealPolicies>::value> has_version;
191             return ignore_excess_digits(first, last, has_version());
192         }
193 
194         template <typename Iterator, typename Attribute>
195         static bool
parseboost::spirit::qi::detail::real_impl196         parse(Iterator& first, Iterator const& last, Attribute& attr,
197             RealPolicies const& p)
198         {
199             if (first == last)
200                 return false;
201             Iterator save = first;
202 
203             // Start by parsing the sign. neg will be true if
204             // we got a "-" sign, false otherwise.
205             bool neg = p.parse_sign(first, last);
206 
207             // Now attempt to parse an integer
208             T n;
209 
210             typename traits::real_accumulator<T>::type acc_n = 0;
211             bool got_a_number = p.parse_n(first, last, acc_n);
212             int excess_n = 0;
213 
214             // If we did not get a number it might be a NaN, Inf or a leading
215             // dot.
216             if (!got_a_number)
217             {
218                 // Check whether the number to parse is a NaN or Inf
219                 if (p.parse_nan(first, last, n) ||
220                     p.parse_inf(first, last, n))
221                 {
222                     // If we got a negative sign, negate the number
223                     traits::assign_to(traits::negate(neg, n), attr);
224                     return true;    // got a NaN or Inf, return early
225                 }
226 
227                 // If we did not get a number and our policies do not
228                 // allow a leading dot, fail and return early (no-match)
229                 if (!p.allow_leading_dot)
230                 {
231                     first = save;
232                     return false;
233                 }
234             }
235             else
236             {
237                 // We got a number and we still see digits. This happens if acc_n (an integer)
238                 // exceeds the integer's capacity. Collect the excess digits.
239                 excess_n = static_cast<int>(ignore_excess_digits(first, last));
240             }
241 
242             bool e_hit = false;
243             Iterator e_pos;
244             int frac_digits = 0;
245 
246             // Try to parse the dot ('.' decimal point)
247             if (p.parse_dot(first, last))
248             {
249                 // We got the decimal point. Now we will try to parse
250                 // the fraction if it is there. If not, it defaults
251                 // to zero (0) only if we already got a number.
252                 if (excess_n != 0)
253                 {
254                     // We skip the fractions if we already exceeded our digits capacity
255                     ignore_excess_digits(first, last);
256                 }
257                 else if (p.parse_frac_n(first, last, acc_n, frac_digits))
258                 {
259                     BOOST_ASSERT(frac_digits >= 0);
260                 }
261                 else if (!got_a_number || !p.allow_trailing_dot)
262                 {
263                     // We did not get a fraction. If we still haven't got a
264                     // number and our policies do not allow a trailing dot,
265                     // return no-match.
266                     first = save;
267                     return false;
268                 }
269 
270                 // Now, let's see if we can parse the exponent prefix
271                 e_pos = first;
272                 e_hit = p.parse_exp(first, last);
273             }
274             else
275             {
276                 // No dot and no number! Return no-match.
277                 if (!got_a_number)
278                 {
279                     first = save;
280                     return false;
281                 }
282 
283                 // If we must expect a dot and we didn't see an exponent
284                 // prefix, return no-match.
285                 e_pos = first;
286                 e_hit = p.parse_exp(first, last);
287                 if (p.expect_dot && !e_hit)
288                 {
289                     first = save;
290                     return false;
291                 }
292             }
293 
294             if (e_hit)
295             {
296                 // We got the exponent prefix. Now we will try to parse the
297                 // actual exponent.
298                 int exp = 0;
299                 if (p.parse_exp_n(first, last, exp))
300                 {
301                     // Got the exponent value. Scale the number by
302                     // exp + excess_n - frac_digits.
303                     if (!traits::scale(exp + excess_n, frac_digits, n, acc_n))
304                         return false;
305                 }
306                 else
307                 {
308                     // If there is no number, disregard the exponent altogether.
309                     // by resetting 'first' prior to the exponent prefix (e|E)
310                     first = e_pos;
311                     // Scale the number by -frac_digits.
312                     bool r = traits::scale(-frac_digits, n, acc_n);
313                     BOOST_VERIFY(r);
314                 }
315             }
316             else if (frac_digits)
317             {
318                 // No exponent found. Scale the number by -frac_digits.
319                 bool r = traits::scale(-frac_digits, n, acc_n);
320                 BOOST_VERIFY(r);
321             }
322             else
323             {
324                 if (excess_n)
325                 {
326                     if (!traits::scale(excess_n, n, acc_n))
327                         return false;
328                 }
329                 else
330                 {
331                     n = static_cast<T>(acc_n);
332                 }
333             }
334 
335             // If we got a negative sign, negate the number
336             traits::assign_to(traits::negate(neg, n), attr);
337 
338             // Success!!!
339             return true;
340         }
341     };
342 
343 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
344 # pragma warning(pop)
345 #endif
346 
347 }}}}
348 
349 #endif
350