/src/poco/Foundation/src/NumericString.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "Poco/NumericString.h" |
17 | | |
18 | | // +++ double conversion +++ |
19 | | // don't collide with standalone double_conversion library |
20 | | #define double_conversion poco_double_conversion |
21 | | #define UNIMPLEMENTED poco_bugcheck |
22 | | #include "double-conversion.h" |
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-to-string.cc" |
30 | | #include "string-to-double.cc" |
31 | | // --- double conversion --- |
32 | | |
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 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 | 0 | { |
47 | | // these cases should never happen, if they do, it's a library bug |
48 | 0 | poco_assert_dbg (precision > 0); |
49 | 0 | poco_assert_dbg (str.length()); |
50 | |
|
51 | 0 | std::string::size_type decSepPos = str.find(decSep); |
52 | 0 | if (decSepPos == std::string::npos) |
53 | 0 | { |
54 | 0 | str.append(1, decSep); |
55 | 0 | decSepPos = str.size() - 1; |
56 | 0 | } |
57 | |
|
58 | 0 | std::string::size_type frac = str.length() - decSepPos - 1; |
59 | |
|
60 | 0 | std::string::size_type ePos = str.find_first_of("eE"); |
61 | 0 | std::unique_ptr<std::string> eStr; |
62 | 0 | if (ePos != std::string::npos) |
63 | 0 | { |
64 | 0 | eStr.reset(new std::string(str.substr(ePos, std::string::npos))); |
65 | 0 | frac -= eStr->length(); |
66 | 0 | str = str.substr(0, str.length() - eStr->length()); |
67 | 0 | } |
68 | |
|
69 | 0 | if (frac != precision) |
70 | 0 | { |
71 | 0 | if (frac < precision) |
72 | 0 | { |
73 | 0 | str.append(precision - frac, '0'); |
74 | 0 | } |
75 | 0 | else if ((frac > precision) && (decSepPos != std::string::npos)) |
76 | 0 | { |
77 | 0 | int pos = static_cast<int>(decSepPos) + 1 + precision; |
78 | 0 | if (str[pos] >= '5') // we must round up |
79 | 0 | { |
80 | 0 | char carry = 0; |
81 | 0 | if(str[--pos] == '9') |
82 | 0 | { |
83 | 0 | str[pos] = '0'; |
84 | 0 | carry = 1; |
85 | 0 | } |
86 | 0 | else |
87 | 0 | { |
88 | 0 | ++str[pos]; |
89 | 0 | carry = 0; |
90 | 0 | } |
91 | 0 | while (--pos >= 0) |
92 | 0 | { |
93 | 0 | if(str[pos] == decSep) continue; |
94 | 0 | if(carry) |
95 | 0 | { |
96 | 0 | if((str[pos] + carry) <= '9') |
97 | 0 | { |
98 | 0 | ++str[pos]; |
99 | 0 | carry = 0; |
100 | 0 | } |
101 | 0 | else |
102 | 0 | { |
103 | 0 | str[pos] = '0'; |
104 | 0 | carry = 1; |
105 | 0 | } |
106 | 0 | } |
107 | 0 | } |
108 | 0 | if (carry) str.insert(str.begin(), 1, '1'); |
109 | 0 | } |
110 | 0 | str = str.substr(0, decSepPos + 1 + precision); |
111 | 0 | } |
112 | 0 | } |
113 | |
|
114 | 0 | if (eStr.get()) str += *eStr; |
115 | |
|
116 | 0 | if (width && (str.length() < width)) str.insert(str.begin(), width - str.length(), prefix); |
117 | 0 | } |
118 | | |
119 | | |
120 | | void insertThousandSep(std::string& str, char thSep, char decSep = '.') |
121 | | /// Inserts thousand separators. |
122 | | /// Used only internally. |
123 | 0 | { |
124 | 0 | poco_assert (decSep != thSep); |
125 | 0 | if (str.size() == 0) return; |
126 | | |
127 | 0 | std::string::size_type exPos = str.find('e'); |
128 | 0 | if (exPos == std::string::npos) exPos = str.find('E'); |
129 | 0 | std::string::size_type decPos = str.find(decSep); |
130 | | // there's no rinsert, using forward iterator to go backwards |
131 | 0 | std::string::iterator it = str.end(); |
132 | 0 | if (exPos != std::string::npos) it -= str.size() - exPos; |
133 | |
|
134 | 0 | if (decPos != std::string::npos) |
135 | 0 | { |
136 | 0 | while (it != str.begin()) |
137 | 0 | { |
138 | 0 | --it; |
139 | 0 | if (*it == decSep) break; |
140 | 0 | } |
141 | 0 | } |
142 | 0 | int thCount = 0; |
143 | 0 | if (it == str.end()) --it; |
144 | 0 | for (; it != str.begin();) |
145 | 0 | { |
146 | 0 | std::string::iterator pos = it; |
147 | 0 | std::string::value_type chr = *it; |
148 | 0 | std::string::value_type prevChr = *--it; |
149 | |
|
150 | 0 | if (!std::isdigit(chr)) continue; |
151 | | |
152 | 0 | if (++thCount == 3 && std::isdigit(prevChr)) |
153 | 0 | it = str.insert(pos, thSep); |
154 | |
|
155 | 0 | if (thCount == 3) thCount = 0; |
156 | 0 | } |
157 | 0 | } |
158 | | |
159 | | |
160 | | } // namespace |
161 | | |
162 | | |
163 | | namespace Poco { |
164 | | |
165 | | |
166 | | void floatToStr(char* buffer, int bufferSize, float value, int lowDec, int highDec) |
167 | 0 | { |
168 | 0 | using namespace double_conversion; |
169 | |
|
170 | 0 | StringBuilder builder(buffer, bufferSize); |
171 | 0 | int flags = DoubleToStringConverter::UNIQUE_ZERO | |
172 | 0 | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; |
173 | 0 | DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, lowDec, highDec, 0, 0); |
174 | 0 | dc.ToShortestSingle(value, &builder); |
175 | 0 | builder.Finalize(); |
176 | 0 | } |
177 | | |
178 | | |
179 | | void floatToFixedStr(char* buffer, int bufferSize, float value, int precision) |
180 | 0 | { |
181 | 0 | using namespace double_conversion; |
182 | |
|
183 | 0 | StringBuilder builder(buffer, bufferSize); |
184 | 0 | int flags = DoubleToStringConverter::UNIQUE_ZERO | |
185 | 0 | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; |
186 | 0 | 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 | 0 | dc.ToFixed(value, precision, &builder); |
188 | 0 | builder.Finalize(); |
189 | 0 | } |
190 | | |
191 | | |
192 | | std::string& floatToStr(std::string& str, float value, int precision, int width, char thSep, char decSep) |
193 | 0 | { |
194 | 0 | if (!decSep) decSep = '.'; |
195 | 0 | if (precision == 0) value = std::floor(value); |
196 | |
|
197 | 0 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
198 | 0 | floatToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); |
199 | 0 | str = buffer; |
200 | |
|
201 | 0 | if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) |
202 | 0 | replaceInPlace(str, '.', decSep); |
203 | |
|
204 | 0 | if (thSep) insertThousandSep(str, thSep, decSep); |
205 | 0 | if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); |
206 | 0 | return str; |
207 | 0 | } |
208 | | |
209 | | |
210 | | std::string& floatToFixedStr(std::string& str, float value, int precision, int width, char thSep, char decSep) |
211 | 0 | { |
212 | 0 | if (!decSep) decSep = '.'; |
213 | 0 | if (precision == 0) value = std::floor(value); |
214 | |
|
215 | 0 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
216 | 0 | floatToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); |
217 | 0 | str = buffer; |
218 | |
|
219 | 0 | if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) |
220 | 0 | replaceInPlace(str, '.', decSep); |
221 | |
|
222 | 0 | if (thSep) insertThousandSep(str, thSep, decSep); |
223 | 0 | if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); |
224 | 0 | return str; |
225 | 0 | } |
226 | | |
227 | | |
228 | | void doubleToStr(char* buffer, int bufferSize, double value, int lowDec, int highDec) |
229 | 0 | { |
230 | 0 | using namespace double_conversion; |
231 | |
|
232 | 0 | StringBuilder builder(buffer, bufferSize); |
233 | 0 | int flags = DoubleToStringConverter::UNIQUE_ZERO | |
234 | 0 | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; |
235 | 0 | DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, lowDec, highDec, 0, 0); |
236 | 0 | dc.ToShortest(value, &builder); |
237 | 0 | builder.Finalize(); |
238 | 0 | } |
239 | | |
240 | | |
241 | | void doubleToFixedStr(char* buffer, int bufferSize, double value, int precision) |
242 | 0 | { |
243 | 0 | using namespace double_conversion; |
244 | |
|
245 | 0 | StringBuilder builder(buffer, bufferSize); |
246 | 0 | int flags = DoubleToStringConverter::UNIQUE_ZERO | |
247 | 0 | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; |
248 | 0 | DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, |
249 | 0 | -std::numeric_limits<double>::digits10, std::numeric_limits<double>::digits10, 0, 0); |
250 | 0 | dc.ToFixed(value, precision, &builder); |
251 | 0 | builder.Finalize(); |
252 | 0 | } |
253 | | |
254 | | |
255 | | std::string& doubleToStr(std::string& str, double value, int precision, int width, char thSep, char decSep) |
256 | 0 | { |
257 | 0 | if (!decSep) decSep = '.'; |
258 | 0 | if (precision == 0) value = std::floor(value); |
259 | |
|
260 | 0 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
261 | 0 | doubleToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); |
262 | |
|
263 | 0 | str = buffer; |
264 | |
|
265 | 0 | if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) |
266 | 0 | replaceInPlace(str, '.', decSep); |
267 | |
|
268 | 0 | if (thSep) insertThousandSep(str, thSep, decSep); |
269 | 0 | if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); |
270 | 0 | return str; |
271 | 0 | } |
272 | | |
273 | | |
274 | | std::string& doubleToFixedStr(std::string& str, double value, int precision, int width, char thSep, char decSep) |
275 | 0 | { |
276 | 0 | if (!decSep) decSep = '.'; |
277 | 0 | if (precision == 0) value = std::floor(value); |
278 | |
|
279 | 0 | char buffer[POCO_MAX_FLT_STRING_LEN]; |
280 | 0 | doubleToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); |
281 | |
|
282 | 0 | str = buffer; |
283 | |
|
284 | 0 | if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) |
285 | 0 | replaceInPlace(str, '.', decSep); |
286 | |
|
287 | 0 | if (thSep) insertThousandSep(str, thSep, decSep); |
288 | 0 | if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); |
289 | 0 | return str; |
290 | 0 | } |
291 | | |
292 | | |
293 | | float strToFloat(const char* str, const char* inf, const char* nan) |
294 | 0 | { |
295 | 0 | using namespace double_conversion; |
296 | |
|
297 | 0 | int processed; |
298 | 0 | int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES | |
299 | 0 | StringToDoubleConverter::ALLOW_TRAILING_SPACES; |
300 | 0 | StringToDoubleConverter converter(flags, 0.0, Single::NaN(), inf, nan); |
301 | 0 | float result = converter.StringToFloat(str, static_cast<int>(strlen(str)), &processed); |
302 | 0 | return result; |
303 | 0 | } |
304 | | |
305 | | |
306 | | double strToDouble(const char* str, const char* inf, const char* nan) |
307 | 13.9k | { |
308 | 13.9k | using namespace double_conversion; |
309 | 13.9k | int processed; |
310 | 13.9k | int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES | |
311 | 13.9k | StringToDoubleConverter::ALLOW_TRAILING_SPACES; |
312 | 13.9k | StringToDoubleConverter converter(flags, 0.0, Double::NaN(), inf, nan); |
313 | 13.9k | double result = converter.StringToDouble(str, static_cast<int>(strlen(str)), &processed); |
314 | 13.9k | return result; |
315 | 13.9k | } |
316 | | |
317 | | |
318 | | bool strToFloat(const std::string& str, float& result, char decSep, char thSep, const char* inf, const char* nan) |
319 | 0 | { |
320 | 0 | using namespace double_conversion; |
321 | |
|
322 | 0 | std::string tmp(str); |
323 | 0 | trimInPlace(tmp); |
324 | 0 | removeInPlace(tmp, thSep); |
325 | 0 | removeInPlace(tmp, 'f'); |
326 | 0 | replaceInPlace(tmp, decSep, '.'); |
327 | 0 | result = strToFloat(tmp.c_str(), inf, nan); |
328 | 0 | return !FPEnvironment::isInfinite(result) && |
329 | 0 | !FPEnvironment::isNaN(result); |
330 | 0 | } |
331 | | |
332 | | |
333 | | bool strToDouble(const std::string& str, double& result, char decSep, char thSep, const char* inf, const char* nan) |
334 | 13.9k | { |
335 | 13.9k | if (str.empty()) return false; |
336 | | |
337 | 13.9k | using namespace double_conversion; |
338 | | |
339 | 13.9k | std::string tmp(str); |
340 | 13.9k | trimInPlace(tmp); |
341 | 13.9k | removeInPlace(tmp, thSep); |
342 | 13.9k | replaceInPlace(tmp, decSep, '.'); |
343 | 13.9k | removeInPlace(tmp, 'f'); |
344 | 13.9k | result = strToDouble(tmp.c_str(), inf, nan); |
345 | 13.9k | return !FPEnvironment::isInfinite(result) && |
346 | 13.9k | !FPEnvironment::isNaN(result); |
347 | 13.9k | } |
348 | | |
349 | | |
350 | | } // namespace Poco |