Coverage Report

Created: 2024-01-11 06:12

/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