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