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, int precision, int width, char prefix = ' ', 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 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, char thSep, 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 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 std::string::iterator pos = it;
147 std::string::value_type chr = *it;
148 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, int bufferSize, float value, int lowDec, int highDec)
167{
168 using namespace double_conversion;
169
170 StringBuilder builder(buffer, bufferSize);
171 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, int bufferSize, float value, int precision)
180{
181 using namespace double_conversion;
182
183 StringBuilder builder(buffer, bufferSize);
184 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, int precision, int width, 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
210std::string& floatToFixedStr(std::string& str, float value, int precision, int width, char thSep, char decSep)
211{
212 if (!decSep) decSep = '.';
213 if (precision == 0) value = std::floor(value);
214
215 char buffer[POCO_MAX_FLT_STRING_LEN];
216 floatToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision);
217 str = buffer;
218
219 if (decSep && (decSep != '.') && (str.find('.') != std::string::npos))
220 replaceInPlace(str, '.', decSep);
221
222 if (thSep) insertThousandSep(str, thSep, decSep);
223 if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.');
224 return str;
225}
226
227
228void doubleToStr(char* buffer, int bufferSize, double value, int lowDec, int highDec)
229{
230 using namespace double_conversion;
231
232 StringBuilder builder(buffer, bufferSize);
233 int flags = DoubleToStringConverter::UNIQUE_ZERO |
234 DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN;
235 DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, lowDec, highDec, 0, 0);
236 dc.ToShortest(value, &builder);
237 builder.Finalize();
238}
239
240
241void doubleToFixedStr(char* buffer, int bufferSize, double value, int precision)
242{
243 using namespace double_conversion;
244
245 StringBuilder builder(buffer, bufferSize);
246 int flags = DoubleToStringConverter::UNIQUE_ZERO |
247 DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN;
248 DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP,
249 -std::numeric_limits<double>::digits10, std::numeric_limits<double>::digits10, 0, 0);
250 dc.ToFixed(value, precision, &builder);
251 builder.Finalize();
252}
253
254
255std::string& doubleToStr(std::string& str, double value, int precision, int width, char thSep, char decSep)
256{
257 if (!decSep) decSep = '.';
258 if (precision == 0) value = std::floor(value);
259
260 char buffer[POCO_MAX_FLT_STRING_LEN];
261 doubleToStr(buffer, POCO_MAX_FLT_STRING_LEN, value);
262
263 str = buffer;
264
265 if (decSep && (decSep != '.') && (str.find('.') != std::string::npos))
266 replaceInPlace(str, '.', decSep);
267
268 if (thSep) insertThousandSep(str, thSep, decSep);
269 if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.');
270 return str;
271}
272
273
274std::string& doubleToFixedStr(std::string& str, double value, int precision, int width, char thSep, char decSep)
275{
276 if (!decSep) decSep = '.';
277 if (precision == 0) value = std::floor(value);
278
279 char buffer[POCO_MAX_FLT_STRING_LEN];
280 doubleToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision);
281
282 str = buffer;
283
284 if (decSep && (decSep != '.') && (str.find('.') != std::string::npos))
285 replaceInPlace(str, '.', decSep);
286
287 if (thSep) insertThousandSep(str, thSep, decSep);
288 if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.');
289 return str;
290}
291
292
293float strToFloat(const char* str, const char* inf, const char* nan)
294{
295 using namespace double_conversion;
296
297 int processed;
298 int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES |
299 StringToDoubleConverter::ALLOW_TRAILING_SPACES;
300 StringToDoubleConverter converter(flags, 0.0, Single::NaN(), inf, nan);
301 float result = converter.StringToFloat(str, static_cast<int>(strlen(str)), &processed);
302 return result;
303}
304
305
306double strToDouble(const char* str, const char* inf, const char* nan)
307{
308 using namespace double_conversion;
309 int processed;
310 int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES |
311 StringToDoubleConverter::ALLOW_TRAILING_SPACES;
312 StringToDoubleConverter converter(flags, 0.0, Double::NaN(), inf, nan);
313 double result = converter.StringToDouble(str, static_cast<int>(strlen(str)), &processed);
314 return result;
315}
316
317
318bool strToFloat(const std::string& str, float& result, char decSep, char thSep, const char* inf, const char* nan)
319{
320 using namespace double_conversion;
321
322 std::string tmp(str);
323 trimInPlace(tmp);
324 removeInPlace(tmp, thSep);
325 removeInPlace(tmp, 'f');
326 replaceInPlace(tmp, decSep, '.');
327 result = strToFloat(tmp.c_str(), inf, nan);
328 return !FPEnvironment::isInfinite(result) &&
329 !FPEnvironment::isNaN(result);
330}
331
332
333bool strToDouble(const std::string& str, double& result, char decSep, char thSep, const char* inf, const char* nan)
334{
335 if (str.empty()) return false;
336
337 using namespace double_conversion;
338
339 std::string tmp(str);
340 trimInPlace(tmp);
341 removeInPlace(tmp, thSep);
342 replaceInPlace(tmp, decSep, '.');
343 removeInPlace(tmp, 'f');
344 result = strToDouble(tmp.c_str(), inf, nan);
345 return !FPEnvironment::isInfinite(result) &&
346 !FPEnvironment::isNaN(result);
347}
348
349
350} // namespace Poco
351