1///////////////////////////////////////////////////////////////
2// Copyright 2013 John Maddock. Distributed under the Boost
3// Software License, Version 1.0. (See accompanying file
4// LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
5//
6// Generic routines for converting floating point values to and from decimal strings.
7// Note that these use "naive" algorithms which result in rounding error - so they
8// do not round trip to and from the string representation (but should only be out
9// in the last bit).
10//
11
12#ifndef BOOST_MP_FLOAT_STRING_CVT_HPP
13#define BOOST_MP_FLOAT_STRING_CVT_HPP
14
15#include <string>
16#include <cctype>
17#include <boost/multiprecision/detail/no_exceptions_support.hpp>
18#include <boost/multiprecision/detail/assert.hpp>
19
20namespace boost { namespace multiprecision { namespace detail {
21
22template <class I>
23inline void round_string_up_at(std::string& s, std::ptrdiff_t pos, I& expon)
24{
25 //
26 // Rounds up a string representation of a number at pos:
27 //
28 if (pos < 0)
29 {
30 s.insert(pos: static_cast<std::string::size_type>(0), n: 1, c: '1');
31 s.erase(pos: s.size() - 1);
32 ++expon;
33 }
34 else if (s[static_cast<std::size_t>(pos)] == '9')
35 {
36 s[static_cast<std::size_t>(pos)] = '0';
37 round_string_up_at(s, pos - 1, expon);
38 }
39 else
40 {
41 if ((pos == 0) && (s[static_cast<std::size_t>(pos)] == '0') && (s.size() == 1))
42 ++expon;
43 ++s[static_cast<std::size_t>(pos)];
44 }
45}
46
47template <class Backend>
48std::string convert_to_string(Backend b, std::streamsize digits, std::ios_base::fmtflags f)
49{
50 using default_ops::eval_convert_to;
51 using default_ops::eval_divide;
52 using default_ops::eval_floor;
53 using default_ops::eval_fpclassify;
54 using default_ops::eval_log10;
55 using default_ops::eval_multiply;
56 using default_ops::eval_pow;
57 using default_ops::eval_subtract;
58
59 using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type;
60 using exponent_type = typename Backend::exponent_type ;
61
62 std::string result;
63 bool iszero = false;
64 bool isneg = false;
65 exponent_type expon = 0;
66 std::streamsize org_digits = digits;
67 BOOST_MP_ASSERT(digits > 0);
68
69 int fpt = eval_fpclassify(b);
70
71 if (fpt == static_cast<int>(FP_ZERO))
72 {
73 result = "0";
74 iszero = true;
75 }
76 else if (fpt == static_cast<int>(FP_INFINITE))
77 {
78 if (b.compare(ui_type(0)) < 0)
79 return "-inf";
80 else
81 return ((f & std::ios_base::showpos) == std::ios_base::showpos) ? "+inf" : "inf";
82 }
83 else if (fpt == static_cast<int>(FP_NAN))
84 {
85 return "nan";
86 }
87 else
88 {
89 //
90 // Start by figuring out the exponent:
91 //
92 isneg = b.compare(ui_type(0)) < 0;
93 if (isneg)
94 b.negate();
95 Backend t;
96 Backend ten;
97 ten = ui_type(10);
98
99 eval_log10(t, b);
100 eval_floor(t, t);
101 eval_convert_to(&expon, t);
102 if (-expon > std::numeric_limits<number<Backend> >::max_exponent10 - 3)
103 {
104 int e = -expon / 2;
105 Backend t2;
106 eval_pow(t2, ten, e);
107 eval_multiply(t, t2, b);
108 eval_multiply(t, t2);
109 if (expon & 1)
110 eval_multiply(t, ten);
111 }
112 else
113 {
114 eval_pow(t, ten, -expon);
115 eval_multiply(t, b);
116 }
117 //
118 // Make sure we're between [1,10) and adjust if not:
119 //
120 if (t.compare(ui_type(1)) < 0)
121 {
122 eval_multiply(t, ui_type(10));
123 --expon;
124 }
125 else if (t.compare(ui_type(10)) >= 0)
126 {
127 eval_divide(t, ui_type(10));
128 ++expon;
129 }
130 Backend digit;
131 ui_type cdigit;
132 //
133 // Adjust the number of digits required based on formatting options:
134 //
135 if (((f & std::ios_base::fixed) == std::ios_base::fixed) && (expon != -1))
136 digits += expon + 1;
137 if ((f & std::ios_base::scientific) == std::ios_base::scientific)
138 ++digits;
139 //
140 // Extract the digits one at a time:
141 //
142 for (unsigned i = 0; i < digits; ++i)
143 {
144 eval_floor(digit, t);
145 eval_convert_to(&cdigit, digit);
146 result += static_cast<char>('0' + cdigit);
147 eval_subtract(t, digit);
148 eval_multiply(t, ten);
149 }
150 //
151 // Possibly round result:
152 //
153 if (digits >= 0)
154 {
155 eval_floor(digit, t);
156 eval_convert_to(&cdigit, digit);
157 eval_subtract(t, digit);
158 if ((cdigit == 5) && (t.compare(ui_type(0)) == 0))
159 {
160 // Bankers rounding:
161 if ((*result.rbegin() - '0') & 1)
162 {
163 round_string_up_at(result, static_cast<std::ptrdiff_t>(result.size() - 1u), expon);
164 }
165 }
166 else if (cdigit >= 5)
167 {
168 round_string_up_at(result, static_cast<std::ptrdiff_t>(result.size() - 1u), expon);
169 }
170 }
171 eval_floor(t, b);
172 if ((t.compare(b) == 0) && (static_cast<std::size_t>(expon + 1) < result.size()))
173 {
174 // Input is an integer, sometimes we get a result which is not an integer here as a result of printing too
175 // many digits, so lets round if required:
176 round_string_up_at(result, expon + 1, expon);
177 result.erase(pos: static_cast<std::string::size_type>(expon + 1));
178 }
179 }
180 while ((static_cast<std::streamsize>(result.size()) > digits) && (result.size() != 0U))
181 {
182 // We may get here as a result of rounding...
183 if (result.size() > 1)
184 result.erase(pos: result.size() - 1);
185 else
186 {
187 if (expon > 0)
188 --expon; // so we put less padding in the result.
189 else
190 ++expon;
191 ++digits;
192 }
193 }
194 BOOST_MP_ASSERT(org_digits >= 0);
195 if (isneg)
196 result.insert(pos: static_cast<std::string::size_type>(0), n: 1, c: '-');
197 format_float_string(result, expon, org_digits, f, iszero);
198
199 return result;
200}
201
202template <class Backend>
203void convert_from_string(Backend& b, const char* p)
204{
205 using default_ops::eval_add;
206 using default_ops::eval_divide;
207 using default_ops::eval_multiply;
208 using default_ops::eval_pow;
209
210 using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type;
211 b = ui_type(0);
212 if (!p || (*p == 0))
213 return;
214
215 bool is_neg = false;
216 bool is_neg_expon = false;
217 constexpr ui_type ten = ui_type(10);
218 typename Backend::exponent_type expon = 0;
219 int digits_seen = 0;
220
221 using limits = std::numeric_limits<number<Backend, et_off>>;
222
223 constexpr int max_digits = limits::is_specialized ? limits::max_digits10 + 1 : INT_MAX;
224
225 if (*p == '+')
226 ++p;
227 else if (*p == '-')
228 {
229 is_neg = true;
230 ++p;
231 }
232 if ((std::strcmp(s1: p, s2: "nan") == 0) || (std::strcmp(s1: p, s2: "NaN") == 0) || (std::strcmp(s1: p, s2: "NAN") == 0))
233 {
234 eval_divide(b, ui_type(0));
235 if (is_neg)
236 b.negate();
237 return;
238 }
239 if ((std::strcmp(s1: p, s2: "inf") == 0) || (std::strcmp(s1: p, s2: "Inf") == 0) || (std::strcmp(s1: p, s2: "INF") == 0))
240 {
241 b = ui_type(1);
242 eval_divide(b, ui_type(0));
243 if (is_neg)
244 b.negate();
245 return;
246 }
247 //
248 // Grab all the leading digits before the decimal point:
249 //
250 while (std::isdigit(*p))
251 {
252 eval_multiply(b, ten);
253 eval_add(b, ui_type(*p - '0'));
254 ++p;
255 ++digits_seen;
256 }
257 if (*p == '.')
258 {
259 //
260 // Grab everything after the point, stop when we've seen
261 // enough digits, even if there are actually more available:
262 //
263 ++p;
264 while (std::isdigit(*p))
265 {
266 eval_multiply(b, ten);
267 eval_add(b, ui_type(*p - '0'));
268 ++p;
269 --expon;
270 if (++digits_seen > max_digits)
271 break;
272 }
273 while (std::isdigit(*p))
274 ++p;
275 }
276 //
277 // Parse the exponent:
278 //
279 if ((*p == 'e') || (*p == 'E'))
280 {
281 ++p;
282 if (*p == '+')
283 ++p;
284 else if (*p == '-')
285 {
286 is_neg_expon = true;
287 ++p;
288 }
289 typename Backend::exponent_type e2 = 0;
290 while (std::isdigit(*p))
291 {
292 e2 *= 10;
293 e2 += (*p - '0');
294 ++p;
295 }
296 if (is_neg_expon)
297 e2 = -e2;
298 expon += e2;
299 }
300 if (expon)
301 {
302 // Scale by 10^expon, note that 10^expon can be
303 // outside the range of our number type, even though the
304 // result is within range, if that looks likely, then split
305 // the calculation in two:
306 Backend t;
307 t = ten;
308 if (expon > limits::min_exponent10 + 2)
309 {
310 eval_pow(t, t, expon);
311 eval_multiply(b, t);
312 }
313 else
314 {
315 eval_pow(t, t, expon + digits_seen + 1);
316 eval_multiply(b, t);
317 t = ten;
318 eval_pow(t, t, -digits_seen - 1);
319 eval_multiply(b, t);
320 }
321 }
322 if (is_neg)
323 b.negate();
324 if (*p)
325 {
326 // Unexpected input in string:
327 BOOST_MP_THROW_EXCEPTION(std::runtime_error("Unexpected characters in string being interpreted as a float128."));
328 }
329}
330
331}}} // namespace boost::multiprecision::detail
332
333#endif
334

source code of boost/libs/multiprecision/include/boost/multiprecision/detail/float_string_cvt.hpp