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 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 | |
120 | void 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 | |
163 | namespace Poco { |
164 | |
165 | |
166 | void 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 | |
179 | void 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 | |
192 | std::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 | |
210 | std::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 | |
228 | void 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 | |
241 | void 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 | |
255 | std::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 | |
274 | std::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 | |
293 | float 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 | |
306 | double 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 | |
318 | bool 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 | |
333 | bool 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 | |