]>
Commit | Line | Data |
---|---|---|
1e59de90 | 1 | // Copyright (c) 2001-2020 Hartmut Kaiser |
7c673cae FG |
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> | |
7c673cae FG |
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 | |
45 | 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 | |
69 | 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); | |
7c673cae FG |
86 | |
87 | // allow for ADL to find the correct overloads for log10 et.al. | |
88 | using namespace std; | |
89 | ||
1e59de90 | 90 | bool precexp_offset = false; |
7c673cae FG |
91 | U dim = 0; |
92 | if (0 == (Policies::fmtflags::fixed & flags) && !traits::test_zero(n)) | |
93 | { | |
94 | dim = log10(n); | |
1e59de90 | 95 | if (dim > 0) |
7c673cae FG |
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); | |
1e59de90 | 99 | |
7c673cae | 100 | dim = static_cast<U>(-exp); |
1e59de90 | 101 | |
11fdf7f2 TL |
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); | |
1e59de90 TL |
107 | } |
108 | else | |
11fdf7f2 | 109 | n *= spirit::traits::pow10<U>(exp); |
1e59de90 TL |
110 | |
111 | if (n < 1.) | |
112 | { | |
113 | n *= 10.; | |
114 | --dim; | |
115 | precexp_offset = true; | |
116 | } | |
7c673cae FG |
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 | ||
1e59de90 TL |
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) | |
7c673cae FG |
136 | { |
137 | fractional_part = floor(fractional_part - precexp); | |
138 | integer_part += 1; // handle rounding overflow | |
1e59de90 TL |
139 | if (integer_part >= 10. && 0 == (Policies::fmtflags::fixed & flags)) |
140 | { | |
141 | integer_part /= 10.; | |
142 | ++dim; | |
143 | } | |
7c673cae FG |
144 | } |
145 | ||
1e59de90 | 146 | // if trailing zeros are to be omitted, normalize the precision and`` |
7c673cae FG |
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 |