1 | // |
2 | // NumericString.h |
3 | // |
4 | // Library: Foundation |
5 | // Package: Core |
6 | // Module: NumericString |
7 | // |
8 | // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/Bugcheck.h" |
16 | |
17 | |
18 | // +++ double conversion +++ |
19 | #define double_conversion poco_double_conversion // don't collide with standalone double_conversion library |
20 | #define UNREACHABLE poco_bugcheck |
21 | #define UNIMPLEMENTED poco_bugcheck |
22 | #include "diy-fp.cc" |
23 | #include "cached-powers.cc" |
24 | #include "bignum-dtoa.cc" |
25 | #include "bignum.cc" |
26 | #include "fast-dtoa.cc" |
27 | #include "fixed-dtoa.cc" |
28 | #include "strtod.cc" |
29 | #include "double-conversion.cc" |
30 | // --- double conversion --- |
31 | |
32 | #include "Poco/NumericString.h" |
33 | poco_static_assert(POCO_MAX_FLT_STRING_LEN == double_conversion::kMaxSignificantDecimalDigits); |
34 | #include "Poco/String.h" |
35 | #include <memory> |
36 | #include <cctype> |
37 | |
38 | |
39 | namespace { |
40 | |
41 | |
42 | void (std::string& str, const int precision, const int width, const char prefix = ' ', const char decSep = '.') |
43 | /// Pads the string with prefix space and postfix 0. |
44 | /// Alternative prefix (e.g. zero instead of space) can be supplied by caller. |
45 | /// Used only internally. |
46 | { |
47 | // these cases should never happen, if they do, it's a library bug |
48 | poco_assert_dbg (precision > 0); |
49 | poco_assert_dbg (str.length()); |
50 | |
51 | std::string::size_type decSepPos = str.find(decSep); |
52 | if (decSepPos == std::string::npos) |
53 | { |
54 | str.append(1, '.'); |
55 | decSepPos = str.size() - 1; |
56 | } |
57 | |
58 | std::string::size_type frac = str.length() - decSepPos - 1; |
59 | |
60 | const std::string::size_type ePos = str.find_first_of("eE" ); |
61 | std::unique_ptr<std::string> eStr; |
62 | if (ePos != std::string::npos) |
63 | { |
64 | eStr.reset(new std::string(str.substr(ePos, std::string::npos))); |
65 | frac -= eStr->length(); |
66 | str = str.substr(0, str.length() - eStr->length()); |
67 | } |
68 | |
69 | if (frac != precision) |
70 | { |
71 | if (frac < precision) |
72 | { |
73 | str.append(precision - frac, '0'); |
74 | } |
75 | else if ((frac > precision) && (decSepPos != std::string::npos)) |
76 | { |
77 | int pos = static_cast<int>(decSepPos) + 1 + precision; |
78 | if (str[pos] >= '5') // we must round up |
79 | { |
80 | char carry = 0; |
81 | if(str[--pos] == '9') |
82 | { |
83 | str[pos] = '0'; |
84 | carry = 1; |
85 | } |
86 | else |
87 | { |
88 | ++str[pos]; |
89 | carry = 0; |
90 | } |
91 | while (--pos >= 0) |
92 | { |
93 | if(str[pos] == decSep) continue; |
94 | if(carry) |
95 | { |
96 | if((str[pos] + carry) <= '9') |
97 | { |
98 | ++str[pos]; |
99 | carry = 0; |
100 | } |
101 | else |
102 | { |
103 | str[pos] = '0'; |
104 | carry = 1; |
105 | } |
106 | } |
107 | } |
108 | if (carry) str.insert(str.begin(), 1, '1'); |
109 | } |
110 | str = str.substr(0, decSepPos + 1 + precision); |
111 | } |
112 | } |
113 | |
114 | if (eStr.get()) str += *eStr; |
115 | |
116 | if (width && (str.length() < width)) str.insert(str.begin(), width - str.length(), prefix); |
117 | } |
118 | |
119 | |
120 | void insertThousandSep(std::string& str, const char thSep, const char decSep = '.') |
121 | /// Inserts thousand separators. |
122 | /// Used only internally. |
123 | { |
124 | poco_assert (decSep != thSep); |
125 | if (str.size() == 0) return; |
126 | |
127 | std::string::size_type exPos = str.find('e'); |
128 | if (exPos == std::string::npos) exPos = str.find('E'); |
129 | const std::string::size_type decPos = str.find(decSep); |
130 | // there's no rinsert, using forward iterator to go backwards |
131 | std::string::iterator it = str.end(); |
132 | if (exPos != std::string::npos) it -= str.size() - exPos; |
133 | |
134 | if (decPos != std::string::npos) |
135 | { |
136 | while (it != str.begin()) |
137 | { |
138 | --it; |
139 | if (*it == decSep) break; |
140 | } |
141 | } |
142 | int thCount = 0; |
143 | if (it == str.end()) --it; |
144 | for (; it != str.begin();) |
145 | { |
146 | const std::string::iterator pos = it; |
147 | const std::string::value_type chr = *it; |
148 | const std::string::value_type prevChr = *--it; |
149 | |
150 | if (!std::isdigit(chr)) continue; |
151 | |
152 | if (++thCount == 3 && std::isdigit(prevChr)) |
153 | it = str.insert(pos, thSep); |
154 | |
155 | if (thCount == 3) thCount = 0; |
156 | } |
157 | } |
158 | |
159 | |
160 | } // namespace |
161 | |
162 | |
163 | namespace Poco { |
164 | |
165 | |
166 | void floatToStr(char* buffer, const int bufferSize, const float value, const int lowDec, const int highDec) |
167 | { |
168 | using namespace double_conversion; |
169 | |
170 | StringBuilder builder(buffer, bufferSize); |
171 | const int flags = DoubleToStringConverter::UNIQUE_ZERO | |
172 | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; |
173 | DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, lowDec, highDec, 0, 0); |
174 | dc.ToShortestSingle(value, &builder); |
175 | builder.Finalize(); |
176 | } |
177 | |
178 | |
179 | void floatToFixedStr(char* buffer, const int bufferSize, const float value, const int precision) |
180 | { |
181 | using namespace double_conversion; |
182 | |
183 | StringBuilder builder(buffer, bufferSize); |
184 | const int flags = DoubleToStringConverter::UNIQUE_ZERO | |
185 | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; |
186 | DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, -std::numeric_limits<float>::digits10, std::numeric_limits<float>::digits10, 0, 0); |
187 | dc.ToFixed(value, precision, &builder); |
188 | builder.Finalize(); |
189 | } |
190 | |
191 | |
192 | std::string& (std::string& str, float value, const int precision, const int width, const char thSep, char decSep) |
193 | { |
194 | if (!decSep) decSep = '.'; |
195 | if (precision == 0) value = std::floor(value); |
196 | |
197 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
198 | floatToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); |
199 | str = buffer; |
200 | |
201 | if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) |
202 | replaceInPlace(str, '.', decSep); |
203 | |
204 | if (thSep) insertThousandSep(str, thSep, decSep); |
205 | if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); |
206 | return str; |
207 | } |
208 | |
209 | std::string floatToStr(float value, const int precision, const int width, const char thSep, char decSep) |
210 | { |
211 | std::string result; |
212 | if (!decSep) decSep = '.'; |
213 | if (precision == 0) value = std::floor(value); |
214 | |
215 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
216 | floatToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); |
217 | result = buffer; |
218 | |
219 | if (decSep && (decSep != '.') && (result.find('.') != std::string::npos)) |
220 | replaceInPlace(result, '.', decSep); |
221 | |
222 | if (thSep) insertThousandSep(result, thSep, decSep); |
223 | if (precision > 0 || width) pad(result, precision, width, ' ', decSep ? decSep : '.'); |
224 | return result; |
225 | } |
226 | |
227 | |
228 | std::string& (std::string& str, float value, const int precision, const int width, const char thSep, char decSep) |
229 | { |
230 | if (!decSep) decSep = '.'; |
231 | if (precision == 0) value = std::floor(value); |
232 | |
233 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
234 | floatToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); |
235 | str = buffer; |
236 | |
237 | if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) |
238 | replaceInPlace(str, '.', decSep); |
239 | |
240 | if (thSep) insertThousandSep(str, thSep, decSep); |
241 | if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); |
242 | return str; |
243 | } |
244 | |
245 | std::string floatToFixedStr(float value, const int precision, const int width, const char thSep, char decSep) |
246 | { |
247 | std::string result; |
248 | if (!decSep) decSep = '.'; |
249 | if (precision == 0) value = std::floor(value); |
250 | |
251 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
252 | floatToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); |
253 | result = buffer; |
254 | |
255 | if (decSep && (decSep != '.') && (result.find('.') != std::string::npos)) |
256 | replaceInPlace(result, '.', decSep); |
257 | |
258 | if (thSep) insertThousandSep(result, thSep, decSep); |
259 | if (precision > 0 || width) pad(result, precision, width, ' ', decSep ? decSep : '.'); |
260 | return result; |
261 | } |
262 | |
263 | |
264 | void doubleToStr(char* buffer, int bufferSize, double value, int lowDec, int highDec) |
265 | { |
266 | using namespace double_conversion; |
267 | |
268 | StringBuilder builder(buffer, bufferSize); |
269 | const int flags = DoubleToStringConverter::UNIQUE_ZERO | |
270 | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; |
271 | DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, lowDec, highDec, 0, 0); |
272 | dc.ToShortest(value, &builder); |
273 | builder.Finalize(); |
274 | } |
275 | |
276 | |
277 | void doubleToFixedStr(char* buffer, const int bufferSize, const double value, const int precision) |
278 | { |
279 | using namespace double_conversion; |
280 | |
281 | StringBuilder builder(buffer, bufferSize); |
282 | const int flags = DoubleToStringConverter::UNIQUE_ZERO | |
283 | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; |
284 | DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, |
285 | -std::numeric_limits<double>::digits10, std::numeric_limits<double>::digits10, 0, 0); |
286 | dc.ToFixed(value, precision, &builder); |
287 | builder.Finalize(); |
288 | } |
289 | |
290 | |
291 | std::string& (std::string& str, double value, int precision, int width, char thSep, char decSep) |
292 | { |
293 | if (!decSep) decSep = '.'; |
294 | if (precision == 0) value = std::floor(value); |
295 | |
296 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
297 | doubleToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); |
298 | |
299 | str = buffer; |
300 | |
301 | if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) |
302 | replaceInPlace(str, '.', decSep); |
303 | |
304 | if (thSep) insertThousandSep(str, thSep, decSep); |
305 | if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); |
306 | return str; |
307 | } |
308 | |
309 | std::string doubleToStr(double value, int precision, int width, char thSep, char decSep) |
310 | { |
311 | std::string result; |
312 | if (!decSep) decSep = '.'; |
313 | if (precision == 0) value = std::floor(value); |
314 | |
315 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
316 | doubleToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); |
317 | |
318 | result = buffer; |
319 | |
320 | if (decSep && (decSep != '.') && (result.find('.') != std::string::npos)) |
321 | replaceInPlace(result, '.', decSep); |
322 | |
323 | if (thSep) insertThousandSep(result, thSep, decSep); |
324 | if (precision > 0 || width) pad(result, precision, width, ' ', decSep ? decSep : '.'); |
325 | return result; |
326 | } |
327 | |
328 | |
329 | std::string& (std::string& str, double value, int precision, int width, char thSep, char decSep) |
330 | { |
331 | if (!decSep) decSep = '.'; |
332 | if (precision == 0) value = std::floor(value); |
333 | |
334 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
335 | doubleToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); |
336 | |
337 | str = buffer; |
338 | |
339 | if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) |
340 | replaceInPlace(str, '.', decSep); |
341 | |
342 | if (thSep) insertThousandSep(str, thSep, decSep); |
343 | if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); |
344 | return str; |
345 | } |
346 | |
347 | std::string doubleToFixedStr(double value, int precision, int width, char thSep, char decSep) |
348 | { |
349 | std::string result; |
350 | if (!decSep) decSep = '.'; |
351 | if (precision == 0) value = std::floor(value); |
352 | |
353 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
354 | doubleToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); |
355 | |
356 | result = buffer; |
357 | |
358 | if (decSep && (decSep != '.') && (result.find('.') != std::string::npos)) |
359 | replaceInPlace(result, '.', decSep); |
360 | |
361 | if (thSep) insertThousandSep(result, thSep, decSep); |
362 | if (precision > 0 || width) pad(result, precision, width, ' ', decSep ? decSep : '.'); |
363 | return result; |
364 | } |
365 | |
366 | float strToFloat(const char* str, const char* inf, const char* nan) |
367 | { |
368 | using namespace double_conversion; |
369 | |
370 | int processed; |
371 | const int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES | |
372 | StringToDoubleConverter::ALLOW_TRAILING_SPACES; |
373 | StringToDoubleConverter converter(flags, 0.0, Single::NaN(), inf, nan); |
374 | return converter.StringToFloat(str, static_cast<int>(strlen(str)), &processed); |
375 | } |
376 | |
377 | |
378 | double strToDouble(const char* str, const char* inf, const char* nan) |
379 | { |
380 | using namespace double_conversion; |
381 | int processed; |
382 | const int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES | |
383 | StringToDoubleConverter::ALLOW_TRAILING_SPACES; |
384 | StringToDoubleConverter converter(flags, 0.0, Double::NaN(), inf, nan); |
385 | return converter.StringToDouble(str, static_cast<int>(strlen(str)), &processed); |
386 | } |
387 | |
388 | |
389 | bool strToFloat(const std::string& str, float& result, char decSep, char thSep, const char* inf, const char* nan) |
390 | { |
391 | if (str.empty()) return false; |
392 | |
393 | using namespace double_conversion; |
394 | |
395 | std::string tmp(str); |
396 | trimInPlace(tmp); |
397 | removeInPlace(tmp, thSep); |
398 | removeInPlace(tmp, 'f'); |
399 | replaceInPlace(tmp, decSep, '.'); |
400 | result = strToFloat(tmp.c_str(), inf, nan); |
401 | return !FPEnvironment::isInfinite(result) && |
402 | !FPEnvironment::isNaN(result); |
403 | } |
404 | |
405 | |
406 | bool strToDouble(const std::string& str, double& result, char decSep, char thSep, const char* inf, const char* nan) |
407 | { |
408 | if (str.empty()) return false; |
409 | |
410 | using namespace double_conversion; |
411 | |
412 | std::string tmp(str); |
413 | trimInPlace(tmp); |
414 | removeInPlace(tmp, thSep); |
415 | replaceInPlace(tmp, decSep, '.'); |
416 | removeInPlace(tmp, 'f'); |
417 | result = strToDouble(tmp.c_str(), inf, nan); |
418 | return !FPEnvironment::isInfinite(result) && |
419 | !FPEnvironment::isNaN(result); |
420 | } |
421 | |
422 | |
423 | } // namespace Poco |
424 | |