1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qlocale_tools_p.h"
6#include "qdoublescanprint_p.h"
7#include "qlocale_p.h"
8#include "qstring.h"
9
10#include <private/qtools_p.h>
11#include <private/qnumeric_p.h>
12
13#include <ctype.h>
14#include <errno.h>
15#include <float.h>
16#include <limits.h>
17#include <math.h>
18#include <stdlib.h>
19#include <time.h>
20
21#include <limits>
22#include <charconv>
23
24#if defined(Q_OS_LINUX) && !defined(__UCLIBC__)
25# include <fenv.h>
26#endif
27
28// Sizes as defined by the ISO C99 standard - fallback
29#ifndef LLONG_MAX
30# define LLONG_MAX Q_INT64_C(0x7fffffffffffffff)
31#endif
32#ifndef LLONG_MIN
33# define LLONG_MIN (-LLONG_MAX - Q_INT64_C(1))
34#endif
35#ifndef ULLONG_MAX
36# define ULLONG_MAX Q_UINT64_C(0xffffffffffffffff)
37#endif
38
39QT_BEGIN_NAMESPACE
40
41using namespace QtMiscUtils;
42
43QT_CLOCALE_HOLDER
44
45void qt_doubleToAscii(double d, QLocaleData::DoubleForm form, int precision,
46 char *buf, qsizetype bufSize,
47 bool &sign, int &length, int &decpt)
48{
49 if (bufSize == 0) {
50 decpt = 0;
51 sign = d < 0;
52 length = 0;
53 return;
54 }
55
56 // Detect special numbers (nan, +/-inf)
57 // We cannot use the high-level API of libdouble-conversion as we need to
58 // apply locale-specific formatting, such as decimal points, grouping
59 // separators, etc. Because of this, we have to check for infinity and NaN
60 // before calling DoubleToAscii.
61 if (qt_is_inf(d)) {
62 sign = d < 0;
63 if (bufSize >= 3) {
64 buf[0] = 'i';
65 buf[1] = 'n';
66 buf[2] = 'f';
67 length = 3;
68 } else {
69 length = 0;
70 }
71 return;
72 } else if (qt_is_nan(d)) {
73 if (bufSize >= 3) {
74 buf[0] = 'n';
75 buf[1] = 'a';
76 buf[2] = 'n';
77 length = 3;
78 } else {
79 length = 0;
80 }
81 return;
82 }
83
84 if (form == QLocaleData::DFSignificantDigits && precision == 0)
85 precision = 1; // 0 significant digits is silently converted to 1
86
87#if !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED)
88 // one digit before the decimal dot, counts as significant digit for DoubleToStringConverter
89 if (form == QLocaleData::DFExponent && precision >= 0)
90 ++precision;
91
92 double_conversion::DoubleToStringConverter::DtoaMode mode;
93 if (precision == QLocale::FloatingPointShortest) {
94 mode = double_conversion::DoubleToStringConverter::SHORTEST;
95 } else if (form == QLocaleData::DFSignificantDigits || form == QLocaleData::DFExponent) {
96 mode = double_conversion::DoubleToStringConverter::PRECISION;
97 } else {
98 mode = double_conversion::DoubleToStringConverter::FIXED;
99 }
100 // libDoubleConversion is limited to 32-bit lengths. It's ok to cap the buffer size,
101 // though, because the library will never write 2GiB of chars as output
102 // (the length out-parameter is just an int, too).
103 const auto boundedBufferSize = static_cast<int>((std::min)(a: bufSize, b: qsizetype(INT_MAX)));
104 double_conversion::DoubleToStringConverter::DoubleToAscii(v: d, mode, requested_digits: precision, buffer: buf,
105 buffer_length: boundedBufferSize,
106 sign: &sign, length: &length, point: &decpt);
107#else // QT_NO_DOUBLECONVERSION || QT_BOOTSTRAPPED
108
109 // Cut the precision at 999, to fit it into the format string. We can't get more than 17
110 // significant digits, so anything after that is mostly noise. You do get closer to the "middle"
111 // of the range covered by the given double with more digits, so to a degree it does make sense
112 // to honor higher precisions. We define that at more than 999 digits that is not the case.
113 if (precision > 999)
114 precision = 999;
115 else if (precision == QLocale::FloatingPointShortest)
116 precision = std::numeric_limits<double>::max_digits10; // snprintf lacks "shortest" mode
117
118 if (isZero(d)) {
119 // Negative zero is expected as simple "0", not "-0". We cannot do d < 0, though.
120 sign = false;
121 buf[0] = '0';
122 length = 1;
123 decpt = 1;
124 return;
125 } else if (d < 0) {
126 sign = true;
127 d = -d;
128 } else {
129 sign = false;
130 }
131
132 const int formatLength = 7; // '%', '.', 3 digits precision, 'f', '\0'
133 char format[formatLength];
134 format[formatLength - 1] = '\0';
135 format[0] = '%';
136 format[1] = '.';
137 format[2] = char((precision / 100) % 10) + '0';
138 format[3] = char((precision / 10) % 10) + '0';
139 format[4] = char(precision % 10) + '0';
140 int extraChars;
141 switch (form) {
142 case QLocaleData::DFDecimal:
143 format[formatLength - 2] = 'f';
144 // <anything> '.' <precision> '\0'
145 extraChars = wholePartSpace(d) + 2;
146 break;
147 case QLocaleData::DFExponent:
148 format[formatLength - 2] = 'e';
149 // '.', 'e', '-', <exponent> '\0'
150 extraChars = 7;
151 break;
152 case QLocaleData::DFSignificantDigits:
153 format[formatLength - 2] = 'g';
154
155 // either the same as in the 'e' case, or '.' and '\0'
156 // precision covers part before '.'
157 extraChars = 7;
158 break;
159 default:
160 Q_UNREACHABLE();
161 }
162
163 QVarLengthArray<char> target(precision + extraChars);
164
165 length = qDoubleSnprintf(target.data(), target.size(), QT_CLOCALE, format, d);
166 int firstSignificant = 0;
167 int decptInTarget = length;
168
169 // Find the first significant digit (not 0), and note any '.' we encounter.
170 // There is no '-' at the front of target because we made sure d > 0 above.
171 while (firstSignificant < length) {
172 if (target[firstSignificant] == '.')
173 decptInTarget = firstSignificant;
174 else if (target[firstSignificant] != '0')
175 break;
176 ++firstSignificant;
177 }
178
179 // If no '.' found so far, search the rest of the target buffer for it.
180 if (decptInTarget == length)
181 decptInTarget = std::find(target.data() + firstSignificant, target.data() + length, '.') -
182 target.data();
183
184 int eSign = length;
185 if (form != QLocaleData::DFDecimal) {
186 // In 'e' or 'g' form, look for the 'e'.
187 eSign = std::find(target.data() + firstSignificant, target.data() + length, 'e') -
188 target.data();
189
190 if (eSign < length) {
191 // If 'e' is found, the final decimal point is determined by the number after 'e'.
192 // Mind that the final decimal point, decpt, is the offset of the decimal point from the
193 // start of the resulting string in buf. It may be negative or larger than bufSize, in
194 // which case the missing digits are zeroes. In the 'e' case decptInTarget is always 1,
195 // as variants of snprintf always generate numbers with one digit before the '.' then.
196 // This is why the final decimal point is offset by 1, relative to the number after 'e'.
197 auto r = qstrntoll(target.data() + eSign + 1, length - eSign - 1, 10);
198 decpt = r.result + 1;
199 Q_ASSERT(r.ok());
200 Q_ASSERT(r.used + eSign + 1 <= length);
201 } else {
202 // No 'e' found, so it's the 'f' form. Variants of snprintf generate numbers with
203 // potentially multiple digits before the '.', but without decimal exponent then. So we
204 // get the final decimal point from the position of the '.'. The '.' itself takes up one
205 // character. We adjust by 1 below if that gets in the way.
206 decpt = decptInTarget - firstSignificant;
207 }
208 } else {
209 // In 'f' form, there can not be an 'e', so it's enough to look for the '.'
210 // (and possibly adjust by 1 below)
211 decpt = decptInTarget - firstSignificant;
212 }
213
214 // Move the actual digits from the snprintf target to the actual buffer.
215 if (decptInTarget > firstSignificant) {
216 // First move the digits before the '.', if any
217 int lengthBeforeDecpt = decptInTarget - firstSignificant;
218 memcpy(buf, target.data() + firstSignificant, qMin(lengthBeforeDecpt, bufSize));
219 if (eSign > decptInTarget && lengthBeforeDecpt < bufSize) {
220 // Then move any remaining digits, until 'e'
221 memcpy(buf + lengthBeforeDecpt, target.data() + decptInTarget + 1,
222 qMin(eSign - decptInTarget - 1, bufSize - lengthBeforeDecpt));
223 // The final length of the output is the distance between the first significant digit
224 // and 'e' minus 1, for the '.', except if the buffer is smaller.
225 length = qMin(eSign - firstSignificant - 1, bufSize);
226 } else {
227 // 'e' was before the decpt or things didn't fit. Don't subtract the '.' from the length.
228 length = qMin(eSign - firstSignificant, bufSize);
229 }
230 } else {
231 if (eSign > firstSignificant) {
232 // If there are any significant digits at all, they are all after the '.' now.
233 // Just copy them straight away.
234 memcpy(buf, target.data() + firstSignificant, qMin(eSign - firstSignificant, bufSize));
235
236 // The decimal point was before the first significant digit, so we were one off above.
237 // Consider 0.1 - buf will be just '1', and decpt should be 0. But
238 // "decptInTarget - firstSignificant" will yield -1.
239 ++decpt;
240 length = qMin(eSign - firstSignificant, bufSize);
241 } else {
242 // No significant digits means the number is just 0.
243 buf[0] = '0';
244 length = 1;
245 decpt = 1;
246 }
247 }
248#endif // QT_NO_DOUBLECONVERSION || QT_BOOTSTRAPPED
249 while (length > 1 && buf[length - 1] == '0') // drop trailing zeroes
250 --length;
251}
252
253QSimpleParsedNumber<double> qt_asciiToDouble(const char *num, qsizetype numLen,
254 StrayCharacterMode strayCharMode)
255{
256 if (numLen <= 0)
257 return {};
258
259 // We have to catch NaN before because we need NaN as marker for "garbage" in the
260 // libdouble-conversion case and, in contrast to libdouble-conversion or sscanf, we don't allow
261 // "-nan" or "+nan"
262 if (char c = *num; numLen >= 3
263 && (c == '-' || c == '+' || c == 'I' || c == 'i' || c == 'N' || c == 'n')) {
264 bool negative = (c == '-');
265 bool hasSign = negative || (c == '+');
266 qptrdiff offset = 0;
267 if (hasSign) {
268 offset = 1;
269 c = num[offset];
270 }
271
272 if (c > '9') {
273 auto lowered = [](char c) {
274 // this will mangle non-letters, but none can become a letter
275 return c | 0x20;
276 };
277
278 // Found a non-digit, so this MUST be either "inf", "+inf", "-inf"
279 // or "nan". Anything else is an invalid parse and we don't need to
280 // feed it to the converter below.
281 if (numLen != offset + 3)
282 return {};
283
284 c = lowered(c);
285 char c2 = lowered(num[offset + 1]);
286 char c3 = lowered(num[offset + 2]);
287 if (c == 'i' && c2 == 'n' && c3 == 'f')
288 return { .result: negative ? -qt_inf() : qt_inf(), .used: offset + 3 };
289 else if (c == 'n' && c2 == 'a' && c3 == 'n' && !hasSign)
290 return { .result: qt_qnan(), .used: 3 };
291 return {};
292 }
293 }
294
295 double d = 0.0;
296 int processed;
297#if !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED)
298 int conv_flags = double_conversion::StringToDoubleConverter::NO_FLAGS;
299 if (strayCharMode == TrailingJunkAllowed) {
300 conv_flags = double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK;
301 } else if (strayCharMode == WhitespacesAllowed) {
302 conv_flags = double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES
303 | double_conversion::StringToDoubleConverter::ALLOW_TRAILING_SPACES;
304 }
305 double_conversion::StringToDoubleConverter conv(conv_flags, 0.0, qt_qnan(), nullptr, nullptr);
306 if (int(numLen) != numLen) {
307 // a number over 2 GB in length is silly, just assume it isn't valid
308 return {};
309 } else {
310 d = conv.StringToDouble(buffer: num, length: int(numLen), processed_characters_count: &processed);
311 }
312
313 if (!qIsFinite(d)) {
314 if (qIsNaN(d)) {
315 // Garbage found. We don't accept it and return 0.
316 return {};
317 } else {
318 // Overflow. That's not OK, but we still return infinity.
319 return { .result: d, .used: -processed };
320 }
321 }
322#else
323 // ::digits10 is 19, but ::max() is 18'446'744'073'709'551'615ULL - go, figure...
324 constexpr auto maxDigitsForULongLong = 1 + std::numeric_limits<unsigned long long>::digits10;
325 // need to ensure that we don't read more than numLen of input:
326 char fmt[1 + maxDigitsForULongLong + 4 + 1];
327 qsnprintf(fmt, sizeof fmt, "%s%llu%s", "%", static_cast<unsigned long long>(numLen), "lf%n");
328
329 if (qDoubleSscanf(num, QT_CLOCALE, fmt, &d, &processed) < 1)
330 processed = 0;
331
332 if ((strayCharMode == TrailingJunkProhibited && processed != numLen) || qIsNaN(d)) {
333 // Implementation defined nan symbol or garbage found. We don't accept it.
334 return {};
335 }
336
337 if (!qIsFinite(d)) {
338 // Overflow. Check for implementation-defined infinity symbols and reject them.
339 // We assume that any infinity symbol has to contain a character that cannot be part of a
340 // "normal" number (that is 0-9, ., -, +, e).
341 for (int i = 0; i < processed; ++i) {
342 char c = num[i];
343 if ((c < '0' || c > '9') && c != '.' && c != '-' && c != '+' && c != 'e' && c != 'E') {
344 // Garbage found
345 return {};
346 }
347 }
348 return { d, -processed };
349 }
350#endif // !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED)
351
352 // Otherwise we would have gotten NaN or sorted it out above.
353 Q_ASSERT(strayCharMode == TrailingJunkAllowed || processed == numLen);
354
355 // Check if underflow has occurred.
356 if (isZero(d)) {
357 for (int i = 0; i < processed; ++i) {
358 if (num[i] >= '1' && num[i] <= '9') {
359 // if a digit before any 'e' is not 0, then a non-zero number was intended.
360 return {.result: d, .used: -processed};
361 } else if (num[i] == 'e' || num[i] == 'E') {
362 break;
363 }
364 }
365 }
366 return { .result: d, .used: processed };
367}
368
369/* Detect base if 0 and, if base is hex or bin, skip over 0x/0b prefixes */
370static auto scanPrefix(const char *p, const char *stop, int base)
371{
372 struct R
373 {
374 const char *next;
375 int base;
376 };
377 if (p < stop && isAsciiDigit(c: *p)) {
378 if (*p == '0') {
379 const char *x_or_b = p + 1;
380 if (x_or_b < stop) {
381 switch (*x_or_b) {
382 case 'b':
383 case 'B':
384 if (base == 0)
385 base = 2;
386 if (base == 2)
387 p += 2;
388 return R{.next: p, .base: base};
389 case 'x':
390 case 'X':
391 if (base == 0)
392 base = 16;
393 if (base == 16)
394 p += 2;
395 return R{.next: p, .base: base};
396 }
397 }
398 if (base == 0)
399 base = 8;
400 } else if (base == 0) {
401 base = 10;
402 }
403 Q_ASSERT(base);
404 }
405 return R{.next: p, .base: base};
406}
407
408static bool isDigitForBase(char d, int base)
409{
410 if (d < '0')
411 return false;
412 if (d - '0' < qMin(a: base, b: 10))
413 return true;
414 if (base > 10) {
415 d |= 0x20; // tolower
416 return d >= 'a' && d < 'a' + base - 10;
417 }
418 return false;
419}
420
421QSimpleParsedNumber<qulonglong> qstrntoull(const char *begin, qsizetype size, int base)
422{
423 const char *p = begin, *const stop = begin + size;
424 while (p < stop && ascii_isspace(c: *p))
425 ++p;
426 unsigned long long result = 0;
427 if (p >= stop || *p == '-')
428 return { };
429 const auto prefix = scanPrefix(p: *p == '+' ? p + 1 : p, stop, base);
430 if (!prefix.base || prefix.next >= stop)
431 return { };
432
433 const auto res = std::from_chars(first: prefix.next, last: stop, value&: result, base: prefix.base);
434 if (res.ec != std::errc{})
435 return { };
436 return { .result: result, .used: res.ptr == prefix.next ? 0 : res.ptr - begin };
437}
438
439QSimpleParsedNumber<qlonglong> qstrntoll(const char *begin, qsizetype size, int base)
440{
441 const char *p = begin, *const stop = begin + size;
442 while (p < stop && ascii_isspace(c: *p))
443 ++p;
444 // Frustratingly, std::from_chars() doesn't cope with a 0x prefix that might
445 // be between the sign and digits, so we have to handle that for it, which
446 // means we can't use its ability to read LLONG_MIN directly; see below.
447 const bool negate = p < stop && *p == '-';
448 if (negate || (p < stop && *p == '+'))
449 ++p;
450
451 const auto prefix = scanPrefix(p, stop, base);
452 // Must check for digit, as from_chars() will accept a sign, which would be
453 // a second sign, that we should reject.
454 if (!prefix.base || prefix.next >= stop || !isDigitForBase(d: *prefix.next, base: prefix.base))
455 return { };
456
457 long long result = 0;
458 auto res = std::from_chars(first: prefix.next, last: stop, value&: result, base: prefix.base);
459 if (negate && res.ec == std::errc::result_out_of_range) {
460 // Maybe LLONG_MIN:
461 unsigned long long check = 0;
462 res = std::from_chars(first: prefix.next, last: stop, value&: check, base: prefix.base);
463 if (res.ec == std::errc{} && check + std::numeric_limits<long long>::min() == 0)
464 return { .result: std::numeric_limits<long long>::min(), .used: res.ptr - begin };
465 return { };
466 }
467 if (res.ec != std::errc{})
468 return { };
469 return { .result: negate ? -result : result, .used: res.ptr - begin };
470}
471
472template <typename Char>
473static Q_ALWAYS_INLINE void qulltoString_helper(qulonglong number, int base, Char *&p)
474{
475 // Performance-optimized code. Compiler can generate faster code when base is known.
476 switch (base) {
477#define BIG_BASE_LOOP(b) \
478 do { \
479 const int r = number % b; \
480 *--p = Char((r < 10 ? '0' : 'a' - 10) + r); \
481 number /= b; \
482 } while (number)
483#ifndef __OPTIMIZE_SIZE__
484# define SMALL_BASE_LOOP(b) \
485 do { \
486 *--p = Char('0' + number % b); \
487 number /= b; \
488 } while (number)
489
490 case 2: SMALL_BASE_LOOP(2); break;
491 case 8: SMALL_BASE_LOOP(8); break;
492 case 10: SMALL_BASE_LOOP(10); break;
493 case 16: BIG_BASE_LOOP(16); break;
494#undef SMALL_BASE_LOOP
495#endif
496 default: BIG_BASE_LOOP(base); break;
497#undef BIG_BASE_LOOP
498 }
499}
500
501// This is technically "qulonglong to ascii", but that name's taken
502QString qulltoBasicLatin(qulonglong number, int base, bool negative)
503{
504 if (number == 0)
505 return QStringLiteral("0");
506 // Length of MIN_LLONG with the sign in front is 65; we never need surrogate pairs.
507 // We do not need a terminator.
508 const unsigned maxlen = 65;
509 static_assert(CHAR_BIT * sizeof(number) + 1 <= maxlen);
510 char16_t buff[maxlen];
511 char16_t *const end = buff + maxlen, *p = end;
512
513 qulltoString_helper<char16_t>(number, base, p);
514 if (negative)
515 *--p = u'-';
516
517 return QString(reinterpret_cast<QChar *>(p), end - p);
518}
519
520QString qulltoa(qulonglong number, int base, const QStringView zero)
521{
522 // Length of MAX_ULLONG in base 2 is 64; and we may need a surrogate pair
523 // per digit. We do not need a terminator.
524 const unsigned maxlen = 128;
525 static_assert(CHAR_BIT * sizeof(number) <= maxlen);
526 char16_t buff[maxlen];
527 char16_t *const end = buff + maxlen, *p = end;
528
529 if (base != 10 || zero == u"0") {
530 qulltoString_helper<char16_t>(number, base, p);
531 } else if (zero.size() && !zero.at(n: 0).isSurrogate()) {
532 const char16_t zeroUcs2 = zero.at(n: 0).unicode();
533 while (number != 0) {
534 *(--p) = unicodeForDigit(digit: number % base, zero: zeroUcs2);
535
536 number /= base;
537 }
538 } else if (zero.size() == 2 && zero.at(n: 0).isHighSurrogate()) {
539 const char32_t zeroUcs4 = QChar::surrogateToUcs4(high: zero.at(n: 0), low: zero.at(n: 1));
540 while (number != 0) {
541 const char32_t digit = unicodeForDigit(digit: number % base, zero: zeroUcs4);
542
543 *(--p) = QChar::lowSurrogate(ucs4: digit);
544 *(--p) = QChar::highSurrogate(ucs4: digit);
545
546 number /= base;
547 }
548 } else { // zero should always be either a non-surrogate or a surrogate pair:
549 Q_UNREACHABLE_RETURN(QString());
550 }
551
552 return QString(reinterpret_cast<QChar *>(p), end - p);
553}
554
555/*!
556 \internal
557
558 Converts the initial portion of the string pointed to by \a s00 to a double,
559 using the 'C' locale. The function sets the pointer pointed to by \a se to
560 point to the character past the last character converted.
561 */
562double qstrntod(const char *s00, qsizetype len, const char **se, bool *ok)
563{
564 auto r = qt_asciiToDouble(num: s00, numLen: len, strayCharMode: TrailingJunkAllowed);
565 if (se)
566 *se = s00 + (r.used < 0 ? -r.used : r.used);
567 if (ok)
568 *ok = r.ok();
569 return r.result;
570}
571
572QString qdtoa(qreal d, int *decpt, int *sign)
573{
574 bool nonNullSign = false;
575 int nonNullDecpt = 0;
576 int length = 0;
577
578 // Some versions of libdouble-conversion like an extra digit, probably for '\0'
579 constexpr qsizetype digits = std::numeric_limits<double>::max_digits10 + 1;
580 char result[digits];
581 qt_doubleToAscii(d, form: QLocaleData::DFSignificantDigits, precision: QLocale::FloatingPointShortest,
582 buf: result, bufSize: digits, sign&: nonNullSign, length, decpt&: nonNullDecpt);
583
584 if (sign)
585 *sign = nonNullSign ? 1 : 0;
586 if (decpt)
587 *decpt = nonNullDecpt;
588
589 return QLatin1StringView(result, length);
590}
591
592static QLocaleData::DoubleForm resolveFormat(int precision, int decpt, qsizetype length)
593{
594 bool useDecimal;
595 if (precision == QLocale::FloatingPointShortest) {
596 // Find out which representation is shorter.
597 // Set bias to everything added to exponent form but not
598 // decimal, minus the converse.
599
600 // Exponent adds separator, sign and two exponents:
601 int bias = 2 + 2;
602 if (length <= decpt && length > 1)
603 ++bias;
604 else if (length == 1 && decpt <= 0)
605 --bias;
606
607 // When 0 < decpt <= length, the forms have equal digit
608 // counts, plus things bias has taken into account;
609 // otherwise decimal form's digit count is right-padded with
610 // zeros to decpt, when decpt is positive, otherwise it's
611 // left-padded with 1 - decpt zeros.
612 if (decpt <= 0)
613 useDecimal = 1 - decpt <= bias;
614 else if (decpt <= length)
615 useDecimal = true;
616 else
617 useDecimal = decpt <= length + bias;
618 } else {
619 // X == decpt - 1, POSIX's P; -4 <= X < P iff -4 < decpt <= P
620 Q_ASSERT(precision >= 0);
621 useDecimal = decpt > -4 && decpt <= (precision ? precision : 1);
622 }
623 return useDecimal ? QLocaleData::DFDecimal : QLocaleData::DFExponent;
624}
625
626static constexpr int digits(int number)
627{
628 Q_ASSERT(number >= 0);
629 if (Q_LIKELY(number < 1000))
630 return number < 10 ? 1 : number < 100 ? 2 : 3;
631 int i = 3;
632 for (number /= 1000; number; number /= 10)
633 ++i;
634 return i;
635}
636
637// Used generically for both QString and QByteArray
638template <typename T>
639static T dtoString(double d, QLocaleData::DoubleForm form, int precision, bool uppercase)
640{
641 // Undocumented: aside from F.P.Shortest, precision < 0 is treated as
642 // default, 6 - same as printf().
643 if (precision != QLocale::FloatingPointShortest && precision < 0)
644 precision = 6;
645
646 using D = std::numeric_limits<double>;
647 // 1 is for the null-terminator
648 constexpr int MaxDigits = 1 + qMax(a: D::max_exponent10, b: D::digits10 - D::min_exponent10);
649
650 // "maxDigits" above is a reasonable estimate, though we may need more due to extra precision
651 int bufSize = 1;
652 if (precision == QLocale::FloatingPointShortest)
653 bufSize += D::max_digits10;
654 else if (form == QLocaleData::DFDecimal && qIsFinite(d))
655 bufSize += wholePartSpace(d: qAbs(t: d)) + precision;
656 else // Add extra digit due to different interpretations of precision.
657 bufSize += qMax(a: 2, b: precision) + 1; // Must also be big enough for "nan" or "inf"
658
659 // Reserve `MaxDigits` on the stack, which is a reasonable estimate;
660 // but we may need more due to extra precision, which we cannot know at compile-time.
661 QVarLengthArray<char, MaxDigits> buffer(bufSize);
662 bool negative = false;
663 int length = 0;
664 int decpt = 0;
665 qt_doubleToAscii(d, form, precision, buf: buffer.data(), bufSize: buffer.size(), sign&: negative, length, decpt);
666 QLatin1StringView view(buffer.data(), length);
667 const bool succinct = form == QLocaleData::DFSignificantDigits;
668 qsizetype total = (negative ? 1 : 0) + length;
669 if (qIsFinite(d)) {
670 if (succinct)
671 form = resolveFormat(precision, decpt, length: view.size());
672
673 switch (form) {
674 case QLocaleData::DFExponent:
675 total += 3; // (.e+) The '.' may not be needed, but we would only overestimate by 1 char
676 // Exponents: we guarantee at least 2
677 total += std::max(a: 2, b: digits(number: std::abs(x: decpt - 1)));
678 // "length - 1" because one of the digits will always be before the decimal point
679 if (int extraPrecision = precision - (length - 1); extraPrecision > 0 && !succinct)
680 total += extraPrecision; // some requested zero-padding
681 break;
682 case QLocaleData::DFDecimal:
683 if (decpt <= 0) // leading "0." and zeros
684 total += 2 - decpt;
685 else if (decpt < length) // just the dot
686 total += 1;
687 else // trailing zeros (and no dot, unless we require extra precision):
688 total += decpt - length;
689
690 if (precision > 0 && !succinct) {
691 // May need trailing zeros to satisfy precision:
692 if (decpt < length)
693 total += std::max(a: 0, b: precision - length + decpt);
694 else // and a dot to separate them:
695 total += 1 + precision;
696 }
697 break;
698 case QLocaleData::DFSignificantDigits:
699 Q_UNREACHABLE(); // Handled earlier
700 }
701 }
702
703 constexpr bool IsQString = std::is_same_v<T, QString>;
704 using Char = std::conditional_t<IsQString, char16_t, char>;
705
706 T result;
707 result.reserve(total);
708
709 if (negative && !isZero(d)) // We don't return "-0"
710 result.append(Char('-'));
711 if (!qIsFinite(d)) {
712 result.append(view);
713 if (uppercase)
714 result = std::move(result).toUpper();
715 } else {
716 switch (form) {
717 case QLocaleData::DFExponent: {
718 result.append(view.first(n: 1));
719 view = view.sliced(pos: 1);
720 if (!view.isEmpty() || (!succinct && precision > 0)) {
721 result.append(Char('.'));
722 result.append(view);
723 if (qsizetype pad = precision - view.size(); !succinct && pad > 0) {
724 for (int i = 0; i < pad; ++i)
725 result.append(Char('0'));
726 }
727 }
728 int exponent = decpt - 1;
729 result.append(Char(uppercase ? 'E' : 'e'));
730 result.append(Char(exponent < 0 ? '-' : '+'));
731 exponent = std::abs(x: exponent);
732 Q_ASSUME(exponent <= D::max_exponent10 + D::max_digits10);
733 int exponentDigits = digits(number: exponent);
734 // C's printf guarantees a two-digit exponent, and so do we:
735 if (exponentDigits == 1)
736 result.append(Char('0'));
737 result.resize(result.size() + exponentDigits);
738 auto location = reinterpret_cast<Char *>(result.end());
739 qulltoString_helper<Char>(exponent, 10, location);
740 break;
741 }
742 case QLocaleData::DFDecimal:
743 if (decpt < 0) {
744 if constexpr (IsQString)
745 result.append(u"0.0");
746 else
747 result.append("0.0");
748 while (++decpt < 0)
749 result.append(Char('0'));
750 result.append(view);
751 if (!succinct) {
752 auto numDecimals = result.size() - 2 - (negative ? 1 : 0);
753 for (qsizetype i = numDecimals; i < precision; ++i)
754 result.append(Char('0'));
755 }
756 } else {
757 if (decpt > view.size()) {
758 result.append(view);
759 const int sign = negative ? 1 : 0;
760 while (result.size() - sign < decpt)
761 result.append(Char('0'));
762 view = {};
763 } else if (decpt) {
764 result.append(view.first(n: decpt));
765 view = view.sliced(pos: decpt);
766 } else {
767 result.append(Char('0'));
768 }
769 if (!view.isEmpty() || (!succinct && view.size() < precision)) {
770 result.append(Char('.'));
771 result.append(view);
772 if (!succinct) {
773 for (qsizetype i = view.size(); i < precision; ++i)
774 result.append(Char('0'));
775 }
776 }
777 }
778 break;
779 case QLocaleData::DFSignificantDigits:
780 Q_UNREACHABLE(); // taken care of earlier
781 break;
782 }
783 }
784 Q_ASSERT(total >= result.size()); // No reallocations are needed
785 return result;
786}
787
788QString qdtoBasicLatin(double d, QLocaleData::DoubleForm form, int precision, bool uppercase)
789{
790 return dtoString<QString>(d, form, precision, uppercase);
791}
792
793QByteArray qdtoAscii(double d, QLocaleData::DoubleForm form, int precision, bool uppercase)
794{
795 return dtoString<QByteArray>(d, form, precision, uppercase);
796}
797
798QT_END_NAMESPACE
799

source code of qtbase/src/corelib/text/qlocale_tools.cpp