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