• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //  Copyright (c) 2001-2020 Hartmut Kaiser
2 //
3 //  Distributed under the Boost Software License, Version 1.0. (See accompanying
4 //  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #if !defined(BOOST_SPIRIT_KARMA_REAL_UTILS_FEB_23_2007_0841PM)
7 #define BOOST_SPIRIT_KARMA_REAL_UTILS_FEB_23_2007_0841PM
8 
9 #if defined(_MSC_VER)
10 #pragma once
11 #endif
12 
13 #include <boost/config.hpp>
14 #include <boost/config/no_tr1/cmath.hpp>
15 #include <boost/detail/workaround.hpp>
16 #include <boost/limits.hpp>
17 
18 #include <boost/spirit/home/support/char_class.hpp>
19 #include <boost/spirit/home/support/unused.hpp>
20 #include <boost/spirit/home/support/detail/pow10.hpp>
21 #include <boost/spirit/home/karma/detail/generate_to.hpp>
22 #include <boost/spirit/home/karma/detail/string_generate.hpp>
23 #include <boost/spirit/home/karma/numeric/detail/numeric_utils.hpp>
24 
25 namespace boost { namespace spirit { namespace karma
26 {
27     ///////////////////////////////////////////////////////////////////////////
28     //
29     //  The real_inserter template takes care of the floating point number to
30     //  string conversion. The Policies template parameter is used to allow
31     //  customization of the formatting process
32     //
33     ///////////////////////////////////////////////////////////////////////////
34     template <typename T>
35     struct real_policies;
36 
37     template <typename T
38       , typename Policies = real_policies<T>
39       , typename CharEncoding = unused_type
40       , typename Tag = unused_type>
41     struct real_inserter
42     {
43         template <typename OutputIterator, typename U>
44         static bool
callboost::spirit::karma::real_inserter45         call (OutputIterator& sink, U n, Policies const& p = Policies())
46         {
47             if (traits::test_nan(n)) {
48                 return p.template nan<CharEncoding, Tag>(
49                     sink, n, p.force_sign(n));
50             }
51             else if (traits::test_infinite(n)) {
52                 return p.template inf<CharEncoding, Tag>(
53                     sink, n, p.force_sign(n));
54             }
55             return p.template call<real_inserter>(sink, n, p);
56         }
57 
58 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
59 # pragma warning(push)
60 # pragma warning(disable: 4100)   // 'p': unreferenced formal parameter
61 # pragma warning(disable: 4127)   // conditional expression is constant
62 # pragma warning(disable: 4267)   // conversion from 'size_t' to 'unsigned int', possible loss of data
63 #endif
64         ///////////////////////////////////////////////////////////////////////
65         //  This is the workhorse behind the real generator
66         ///////////////////////////////////////////////////////////////////////
67         template <typename OutputIterator, typename U>
68         static bool
call_nboost::spirit::karma::real_inserter69         call_n (OutputIterator& sink, U n, Policies const& p)
70         {
71         // prepare sign and get output format
72             bool force_sign = p.force_sign(n);
73             bool sign_val = false;
74             int flags = p.floatfield(n);
75             if (traits::test_negative(n))
76             {
77                 n = -n;
78                 sign_val = true;
79             }
80 
81         // The scientific representation requires the normalization of the
82         // value to convert.
83 
84             // get correct precision for generated number
85             unsigned precision = p.precision(n);
86 
87             // allow for ADL to find the correct overloads for log10 et.al.
88             using namespace std;
89 
90             bool precexp_offset = false;
91             U dim = 0;
92             if (0 == (Policies::fmtflags::fixed & flags) && !traits::test_zero(n))
93             {
94                 dim = log10(n);
95                 if (dim > 0)
96                     n /= spirit::traits::pow10<U>(traits::truncate_to_long::call(dim));
97                 else if (n < 1.) {
98                     long exp = traits::truncate_to_long::call(-dim);
99 
100                     dim = static_cast<U>(-exp);
101 
102                     // detect and handle denormalized numbers to prevent overflow in pow10
103                     if (exp > std::numeric_limits<U>::max_exponent10)
104                     {
105                         n *= spirit::traits::pow10<U>(std::numeric_limits<U>::max_exponent10);
106                         n *= spirit::traits::pow10<U>(exp - std::numeric_limits<U>::max_exponent10);
107                     }
108                     else
109                         n *= spirit::traits::pow10<U>(exp);
110 
111                     if (n < 1.)
112                     {
113                         n *= 10.;
114                         --dim;
115                         precexp_offset = true;
116                     }
117                 }
118             }
119 
120         // prepare numbers (sign, integer and fraction part)
121             U integer_part;
122             U precexp = spirit::traits::pow10<U>(precision);
123             U fractional_part = modf(n, &integer_part);
124 
125             if (precexp_offset)
126             {
127                 fractional_part =
128                     floor((fractional_part * precexp + U(0.5)) * U(10.)) / U(10.);
129             }
130             else
131             {
132                 fractional_part = floor(fractional_part * precexp + U(0.5));
133             }
134 
135             if (fractional_part >= precexp)
136             {
137                 fractional_part = floor(fractional_part - precexp);
138                 integer_part += 1;    // handle rounding overflow
139                 if (integer_part >= 10.)
140                 {
141                     integer_part /= 10.;
142                     ++dim;
143                 }
144             }
145 
146         // if trailing zeros are to be omitted, normalize the precision and``
147         // fractional part
148             U long_int_part = floor(integer_part);
149             U long_frac_part = fractional_part;
150             unsigned prec = precision;
151             if (!p.trailing_zeros(n))
152             {
153                 U frac_part_floor = long_frac_part;
154                 if (0 != long_frac_part) {
155                     // remove the trailing zeros
156                     while (0 != prec &&
157                            0 == traits::remainder<10>::call(long_frac_part))
158                     {
159                         long_frac_part = traits::divide<10>::call(long_frac_part);
160                         --prec;
161                     }
162                 }
163                 else {
164                     // if the fractional part is zero, we don't need to output
165                     // any additional digits
166                     prec = 0;
167                 }
168 
169                 if (precision != prec)
170                 {
171                     long_frac_part = frac_part_floor /
172                         spirit::traits::pow10<U>(precision-prec);
173                 }
174             }
175 
176         // call the actual generating functions to output the different parts
177             if ((force_sign || sign_val) &&
178                 traits::test_zero(long_int_part) &&
179                 traits::test_zero(long_frac_part))
180             {
181                 sign_val = false;     // result is zero, no sign please
182                 force_sign = false;
183             }
184 
185         // generate integer part
186             bool r = p.integer_part(sink, long_int_part, sign_val, force_sign);
187 
188         // generate decimal point
189             r = r && p.dot(sink, long_frac_part, precision);
190 
191         // generate fractional part with the desired precision
192             r = r && p.fraction_part(sink, long_frac_part, prec, precision);
193 
194             if (r && 0 == (Policies::fmtflags::fixed & flags)) {
195                 return p.template exponent<CharEncoding, Tag>(sink,
196                     traits::truncate_to_long::call(dim));
197             }
198             return r;
199         }
200 
201 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
202 # pragma warning(pop)
203 #endif
204 
205     };
206 }}}
207 
208 #endif
209 
210