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"
33poco_static_assert(POCO_MAX_FLT_STRING_LEN == double_conversion::kMaxSignificantDecimalDigits);
34#include "Poco/String.h"
35#include <memory>
36#include <cctype>
37
38
39namespace {
40
41
42void pad(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
120void 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
163namespace Poco {
164
165
166void 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
179void 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
192std::string& floatToStr(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
209std::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
228std::string& floatToFixedStr(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
245std::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
264void 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
277void 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
291std::string& doubleToStr(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
309std::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
329std::string& doubleToFixedStr(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
347std::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
366float 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
378double 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
389bool 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
406bool 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