1//
2// NumericString.h
3//
4// Library: Foundation
5// Package: Core
6// Module: NumericString
7//
8// Numeric string utility functions.
9//
10// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
11// and Contributors.
12//
13// SPDX-License-Identifier: BSL-1.0
14//
15
16
17#ifndef Foundation_NumericString_INCLUDED
18#define Foundation_NumericString_INCLUDED
19
20
21#include "Poco/Foundation.h"
22#include "Poco/Buffer.h"
23#include "Poco/FPEnvironment.h"
24#ifdef min
25 #undef min
26#endif
27#ifdef max
28 #undef max
29#endif
30#include <limits>
31#include <cmath>
32#include <cctype>
33#if !defined(POCO_NO_LOCALE)
34 #include <locale>
35#endif
36
37
38// binary numbers are supported, thus 64 (bits) + 1 (string terminating zero)
39#define POCO_MAX_INT_STRING_LEN 65
40// value from strtod.cc (double_conversion::kMaxSignificantDecimalDigits)
41#define POCO_MAX_FLT_STRING_LEN 780
42
43#define POCO_FLT_INF "inf"
44#define POCO_FLT_NAN "nan"
45#define POCO_FLT_EXP 'e'
46
47
48namespace Poco {
49
50
51inline char decimalSeparator()
52 /// Returns decimal separator from global locale or
53 /// default '.' for platforms where locale is unavailable.
54{
55#if !defined(POCO_NO_LOCALE)
56 return std::use_facet<std::numpunct<char> >(std::locale()).decimal_point();
57#else
58 return '.';
59#endif
60}
61
62
63inline char thousandSeparator()
64 /// Returns thousand separator from global locale or
65 /// default ',' for platforms where locale is unavailable.
66{
67#if !defined(POCO_NO_LOCALE)
68 return std::use_facet<std::numpunct<char> >(std::locale()).thousands_sep();
69#else
70 return ',';
71#endif
72}
73
74
75//
76// String to Number Conversions
77//
78
79template <typename I>
80bool strToInt(const char* pStr, I& result, short base, char thSep = ',')
81 /// Converts zero-terminated character array to integer number;
82 /// Thousand separators are recognized for base10 and current locale;
83 /// it is silently skipped but not verified for correct positioning.
84 /// Function returns true if successful. If parsing was unsuccessful,
85 /// the return value is false with the result value undetermined.
86{
87 if (!pStr) return false;
88 while (std::isspace(*pStr)) ++pStr;
89 if (*pStr == '\0') return false;
90 short sign = 1;
91 if ((base == 10) && (*pStr == '-'))
92 {
93 // Unsigned types can't be negative so abort parsing
94 if (std::numeric_limits<I>::min() >= 0) return false;
95 sign = -1;
96 ++pStr;
97 }
98 else if (*pStr == '+') ++pStr;
99
100 // parser states:
101 const char STATE_SIGNIFICANT_DIGITS = 1;
102 char state = 0;
103
104 result = 0;
105 I limitCheck = std::numeric_limits<I>::max() / base;
106 for (; *pStr != '\0'; ++pStr)
107 {
108 switch (*pStr)
109 {
110 case '0':
111 if (state < STATE_SIGNIFICANT_DIGITS) break;
112
113 case '1': case '2': case '3': case '4':
114 case '5': case '6': case '7':
115 if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
116 if (result > limitCheck) return false;
117 result = result * base + (*pStr - '0');
118
119 break;
120
121 case '8': case '9':
122 if ((base == 10) || (base == 0x10))
123 {
124 if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
125 if (result > limitCheck) return false;
126 result = result * base + (*pStr - '0');
127 }
128 else return false;
129
130 break;
131
132 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
133 if (base != 0x10) return false;
134 if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
135 if (result > limitCheck) return false;
136 result = result * base + (10 + *pStr - 'a');
137
138 break;
139
140 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
141 if (base != 0x10) return false;
142 if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
143 if (result > limitCheck) return false;
144 result = result * base + (10 + *pStr - 'A');
145
146 break;
147
148 case '.':
149 if ((base == 10) && (thSep == '.')) break;
150 else return false;
151
152 case ',':
153 if ((base == 10) && (thSep == ',')) break;
154 else return false;
155
156 case ' ':
157 if ((base == 10) && (thSep == ' ')) break;
158 // fallthrough
159
160 default:
161 return false;
162 }
163 }
164
165 if ((sign < 0) && (base == 10)) result *= sign;
166
167 return true;
168}
169
170
171template <typename I>
172bool strToInt(const std::string& str, I& result, short base, char thSep = ',')
173 /// Converts string to integer number;
174 /// This is a wrapper function, for details see see the
175 /// bool strToInt(const char*, I&, short, char) implementation.
176{
177 return strToInt(str.c_str(), result, base, thSep);
178}
179
180
181//
182// Number to String Conversions
183//
184
185namespace Impl {
186
187 class Ptr
188 /// Utility char pointer wrapper class.
189 /// Class ensures increment/decrement remain within boundaries.
190 {
191 public:
192 Ptr(char* ptr, std::size_t offset): _beg(ptr), _cur(ptr), _end(ptr + offset)
193 {
194 }
195
196 char*& operator ++ () // prefix
197 {
198 check(_cur + 1);
199 return ++_cur;
200 }
201
202 char* operator ++ (int) // postfix
203 {
204 check(_cur + 1);
205 char* tmp = _cur++;
206 return tmp;
207 }
208
209 char*& operator -- () // prefix
210 {
211 check(_cur - 1);
212 return --_cur;
213 }
214
215 char* operator -- (int) // postfix
216 {
217 check(_cur - 1);
218 char* tmp = _cur--;
219 return tmp;
220 }
221
222 char*& operator += (int incr)
223 {
224 check(_cur + incr);
225 return _cur += incr;
226 }
227
228 char*& operator -= (int decr)
229 {
230 check(_cur - decr);
231 return _cur -= decr;
232 }
233
234 operator char* () const
235 {
236 return _cur;
237 }
238
239 std::size_t span() const
240 {
241 return _end - _beg;
242 }
243
244 private:
245 void check(char* ptr)
246 {
247 if (ptr > _end) throw RangeException();
248 }
249
250 const char* _beg;
251 char* _cur;
252 const char* _end;
253};
254
255} // namespace Impl
256
257
258template <typename T>
259bool intToStr(T value,
260 unsigned short base,
261 char* result,
262 std::size_t& size,
263 bool prefix = false,
264 int width = -1,
265 char fill = ' ',
266 char thSep = 0)
267 /// Converts integer to string. Numeric bases from binary to hexadecimal are supported.
268 /// If width is non-zero, it pads the return value with fill character to the specified width.
269 /// When padding is zero character ('0'), it is prepended to the number itself; all other
270 /// paddings are prepended to the formatted result with minus sign or base prefix included
271 /// If prefix is true and base is octal or hexadecimal, respective prefix ('0' for octal,
272 /// "0x" for hexadecimal) is prepended. For all other bases, prefix argument is ignored.
273 /// Formatted string has at least [width] total length.
274{
275 if (base < 2 || base > 0x10)
276 {
277 *result = '\0';
278 return false;
279 }
280
281 Impl::Ptr ptr(result, size);
282 int thCount = 0;
283 T tmpVal;
284 do
285 {
286 tmpVal = value;
287 value /= base;
288 *ptr++ = "FEDCBA9876543210123456789ABCDEF"[15 + (tmpVal - value * base)];
289 if (thSep && (base == 10) && (++thCount == 3))
290 {
291 *ptr++ = thSep;
292 thCount = 0;
293 }
294 } while (value);
295
296 if ('0' == fill)
297 {
298 if (tmpVal < 0) --width;
299 if (prefix && base == 010) --width;
300 if (prefix && base == 0x10) width -= 2;
301 while ((ptr - result) < width) *ptr++ = fill;
302 }
303
304 if (prefix && base == 010) *ptr++ = '0';
305 else if (prefix && base == 0x10)
306 {
307 *ptr++ = 'x';
308 *ptr++ = '0';
309 }
310
311 if (tmpVal < 0) *ptr++ = '-';
312
313 if ('0' != fill)
314 {
315 while ((ptr - result) < width) *ptr++ = fill;
316 }
317
318 size = ptr - result;
319 poco_assert_dbg (size <= ptr.span());
320 poco_assert_dbg ((-1 == width) || (size >= size_t(width)));
321 *ptr-- = '\0';
322
323 char* ptrr = result;
324 char tmp;
325 while(ptrr < ptr)
326 {
327 tmp = *ptr;
328 *ptr-- = *ptrr;
329 *ptrr++ = tmp;
330 }
331
332 return true;
333}
334
335
336template <typename T>
337bool uIntToStr(T value,
338 unsigned short base,
339 char* result,
340 std::size_t& size,
341 bool prefix = false,
342 int width = -1,
343 char fill = ' ',
344 char thSep = 0)
345 /// Converts unsigned integer to string. Numeric bases from binary to hexadecimal are supported.
346 /// If width is non-zero, it pads the return value with fill character to the specified width.
347 /// When padding is zero character ('0'), it is prepended to the number itself; all other
348 /// paddings are prepended to the formatted result with minus sign or base prefix included
349 /// If prefix is true and base is octal or hexadecimal, respective prefix ('0' for octal,
350 /// "0x" for hexadecimal) is prepended. For all other bases, prefix argument is ignored.
351 /// Formatted string has at least [width] total length.
352{
353 if (base < 2 || base > 0x10)
354 {
355 *result = '\0';
356 return false;
357 }
358
359 Impl::Ptr ptr(result, size);
360 int thCount = 0;
361 T tmpVal;
362 do
363 {
364 tmpVal = value;
365 value /= base;
366 *ptr++ = "FEDCBA9876543210123456789ABCDEF"[15 + (tmpVal - value * base)];
367 if (thSep && (base == 10) && (++thCount == 3))
368 {
369 *ptr++ = thSep;
370 thCount = 0;
371 }
372 } while (value);
373
374 if ('0' == fill)
375 {
376 if (prefix && base == 010) --width;
377 if (prefix && base == 0x10) width -= 2;
378 while ((ptr - result) < width) *ptr++ = fill;
379 }
380
381 if (prefix && base == 010) *ptr++ = '0';
382 else if (prefix && base == 0x10)
383 {
384 *ptr++ = 'x';
385 *ptr++ = '0';
386 }
387
388 if ('0' != fill)
389 {
390 while ((ptr - result) < width) *ptr++ = fill;
391 }
392
393 size = ptr - result;
394 poco_assert_dbg (size <= ptr.span());
395 poco_assert_dbg ((-1 == width) || (size >= size_t(width)));
396 *ptr-- = '\0';
397
398 char* ptrr = result;
399 char tmp;
400 while(ptrr < ptr)
401 {
402 tmp = *ptr;
403 *ptr-- = *ptrr;
404 *ptrr++ = tmp;
405 }
406
407 return true;
408}
409
410
411template <typename T>
412bool intToStr (T number, unsigned short base, std::string& result, bool prefix = false, int width = -1, char fill = ' ', char thSep = 0)
413 /// Converts integer to string; This is a wrapper function, for details see see the
414 /// bool intToStr(T, unsigned short, char*, int, int, char, char) implementation.
415{
416 char res[POCO_MAX_INT_STRING_LEN] = {0};
417 std::size_t size = POCO_MAX_INT_STRING_LEN;
418 bool ret = intToStr(number, base, res, size, prefix, width, fill, thSep);
419 result.assign(res, size);
420 return ret;
421}
422
423
424template <typename T>
425bool uIntToStr (T number, unsigned short base, std::string& result, bool prefix = false, int width = -1, char fill = ' ', char thSep = 0)
426 /// Converts unsigned integer to string; This is a wrapper function, for details see see the
427 /// bool uIntToStr(T, unsigned short, char*, int, int, char, char) implementation.
428{
429 char res[POCO_MAX_INT_STRING_LEN] = {0};
430 std::size_t size = POCO_MAX_INT_STRING_LEN;
431 bool ret = uIntToStr(number, base, res, size, prefix, width, fill, thSep);
432 result.assign(res, size);
433 return ret;
434}
435
436
437//
438// Wrappers for double-conversion library (http://code.google.com/p/double-conversion/).
439//
440// Library is the implementation of the algorithm described in Florian Loitsch's paper:
441// http://florian.loitsch.com/publications/dtoa-pldi2010.pdf
442//
443
444
445Foundation_API void floatToStr(char* buffer,
446 int bufferSize,
447 float value,
448 int lowDec = -std::numeric_limits<float>::digits10,
449 int highDec = std::numeric_limits<float>::digits10);
450 /// Converts a float value to string. Converted string must be shorter than bufferSize.
451 /// Conversion is done by computing the shortest string of digits that correctly represents
452 /// the input number. Depending on lowDec and highDec values, the function returns
453 /// decimal or exponential representation.
454
455Foundation_API void floatToFixedStr(char* buffer,
456 int bufferSize,
457 float value,
458 int precision);
459 /// Converts a float value to string. Converted string must be shorter than bufferSize.
460 /// Computes a decimal representation with a fixed number of digits after the
461 /// decimal point.
462
463
464Foundation_API std::string& floatToStr(std::string& str,
465 float value,
466 int precision = -1,
467 int width = 0,
468 char thSep = 0,
469 char decSep = 0);
470 /// Converts a float value, assigns it to the supplied string and returns the reference.
471 /// This function calls floatToStr(char*, int, float, int, int) and formats the result according to
472 /// precision (total number of digits after the decimal point, -1 means ignore precision argument)
473 /// and width (total length of formatted string).
474
475
476Foundation_API std::string& floatToFixedStr(std::string& str,
477 float value,
478 int precision,
479 int width = 0,
480 char thSep = 0,
481 char decSep = 0);
482 /// Converts a float value, assigns it to the supplied string and returns the reference.
483 /// This function calls floatToFixedStr(char*, int, float, int) and formats the result according to
484 /// precision (total number of digits after the decimal point) and width (total length of formatted string).
485
486
487Foundation_API void doubleToStr(char* buffer,
488 int bufferSize,
489 double value,
490 int lowDec = -std::numeric_limits<double>::digits10,
491 int highDec = std::numeric_limits<double>::digits10);
492 /// Converts a double value to string. Converted string must be shorter than bufferSize.
493 /// Conversion is done by computing the shortest string of digits that correctly represents
494 /// the input number. Depending on lowDec and highDec values, the function returns
495 /// decimal or exponential representation.
496
497
498Foundation_API void doubleToFixedStr(char* buffer,
499 int bufferSize,
500 double value,
501 int precision);
502 /// Converts a double value to string. Converted string must be shorter than bufferSize.
503 /// Computes a decimal representation with a fixed number of digits after the
504 /// decimal point.
505
506
507Foundation_API std::string& doubleToStr(std::string& str,
508 double value,
509 int precision = -1,
510 int width = 0,
511 char thSep = 0,
512 char decSep = 0);
513 /// Converts a double value, assigns it to the supplied string and returns the reference.
514 /// This function calls doubleToStr(char*, int, double, int, int) and formats the result according to
515 /// precision (total number of digits after the decimal point, -1 means ignore precision argument)
516 /// and width (total length of formatted string).
517
518
519Foundation_API std::string& doubleToFixedStr(std::string& str,
520 double value,
521 int precision = -1,
522 int width = 0,
523 char thSep = 0,
524 char decSep = 0);
525 /// Converts a double value, assigns it to the supplied string and returns the reference.
526 /// This function calls doubleToFixedStr(char*, int, double, int) and formats the result according to
527 /// precision (total number of digits after the decimal point) and width (total length of formatted string).
528
529
530Foundation_API float strToFloat(const char* str,
531 const char* inf = POCO_FLT_INF, const char* nan = POCO_FLT_NAN);
532 /// Converts the string of characters into single-precision floating point number.
533 /// Function uses double_convesrion::DoubleToStringConverter to do the conversion.
534
535
536Foundation_API bool strToFloat(const std::string&, float& result,
537 char decSep = '.', char thSep = ',',
538 const char* inf = POCO_FLT_INF, const char* nan = POCO_FLT_NAN);
539 /// Converts the string of characters into single-precision floating point number.
540 /// The conversion result is assigned to the result parameter.
541 /// If decimal separator and/or thousand separator are different from defaults, they should be
542 /// supplied to ensure proper conversion.
543 ///
544 /// Returns true if successful, false otherwise.
545
546
547Foundation_API double strToDouble(const char* str,
548 const char* inf = POCO_FLT_INF, const char* nan = POCO_FLT_NAN);
549 /// Converts the string of characters into double-precision floating point number.
550
551
552Foundation_API bool strToDouble(const std::string& str, double& result,
553 char decSep = '.', char thSep = ',',
554 const char* inf = POCO_FLT_INF, const char* nan = POCO_FLT_NAN);
555 /// Converts the string of characters into double-precision floating point number.
556 /// The conversion result is assigned to the result parameter.
557 /// If decimal separator and/or thousand separator are different from defaults, they should be
558 /// supplied to ensure proper conversion.
559 ///
560 /// Returns true if successful, false otherwise.
561
562
563} // namespace Poco
564
565
566#endif // Foundation_NumericString_INCLUDED
567