diff --git a/src/corelib/text/qlocale_tools.cpp b/src/corelib/text/qlocale_tools.cpp index 660216a65c1..52124672ca6 100644 --- a/src/corelib/text/qlocale_tools.cpp +++ b/src/corelib/text/qlocale_tools.cpp @@ -807,4 +807,78 @@ QByteArray qdtoAscii(double d, QLocaleData::DoubleForm form, int precision, bool return dtoString(d, form, precision, uppercase); } +#if defined(QT_SUPPORTS_INT128) || (defined(Q_CC_MSVC) && (_MSC_VER >= 1930)) +static inline quint64 toUInt64(qinternaluint128 v) +{ +#ifdef Q_CC_MSVC + return quint64(v._Word[0]); +#else + return quint64(v); +#endif +} +QString quint128toBasicLatin(qinternaluint128 number, int base) +{ + // We divide our 128-bit number into parts that we can do text + // concatenation with. This list is the maximum power of the + // base that is less than 2^64. + static constexpr auto dividers = []() constexpr { + std::array bases {}; + for (int base = 2; base <= 36; ++base) { + quint64 v = base; + while (v * base > v) + v *= base; + bases[base - 2] = v; + } + return bases; + }(); + static constexpr auto digitCounts = []() constexpr { + std::array digits{}; + for (int base = 2; base <= 36; ++base) { + quint64 v = base; + int i = 0; + for (i = 0; v * base > v; ++i) + v *= base; + digits[base - 2] = i; + } + return digits; + }(); + + QString result; + + constexpr unsigned flags = QLocaleData::NoFlags; + const QLocaleData *dd = QLocaleData::c(); + + // special base cases: + constexpr int Width = -1; + if (base == 2 || base == 4 || base == 16) { + // 2^64 is a power of 2, 4 and 16 + result = dd->unsLongLongToString(quint64(number), 64, base, Width, flags); + result.prepend(dd->unsLongLongToString(quint64(number >> 64), -1, base, Width, flags)); + } else { + int digitCount = digitCounts[base - 2]; + quint64 divider = dividers[base - 2]; + quint64 lower = toUInt64(number % divider); + number /= divider; + while (number) { + result.prepend(dd->unsLongLongToString(lower, digitCount, base, Width, flags)); + lower = toUInt64(number % divider); + number /= divider; + } + result.prepend(dd->unsLongLongToString(lower, -1, base, Width, flags)); + } + return result; +} + +QString qint128toBasicLatin(qinternalint128 number, int base) +{ + const bool negative = number < 0; + if (negative) + number *= -1; + QString result = quint128toBasicLatin(qinternaluint128(number), base); + if (negative) + result.prepend(u'-'); + return result; +} +#endif // defined(QT_SUPPORTS_INT128) || (defined(Q_CC_MSVC) && (_MSC_VER >= 1930)) + QT_END_NAMESPACE diff --git a/src/corelib/text/qlocale_tools_p.h b/src/corelib/text/qlocale_tools_p.h index cd012e7afd3..ef1e156f50b 100644 --- a/src/corelib/text/qlocale_tools_p.h +++ b/src/corelib/text/qlocale_tools_p.h @@ -20,6 +20,15 @@ QT_BEGIN_NAMESPACE +#if defined(QT_SUPPORTS_INT128) +using qinternalint128 = qint128; +using qinternaluint128 = quint128; +#elif defined(Q_CC_MSVC) && (_MSC_VER >= 1930) +#include <__msvc_int128.hpp> +using qinternalint128 = std::_Signed128; +using qinternaluint128 = std::_Unsigned128; +#endif + enum StrayCharacterMode { TrailingJunkProhibited, TrailingJunkAllowed, @@ -43,6 +52,13 @@ void qt_doubleToAscii(double d, QLocaleData::DoubleForm form, int precision, [[nodiscard]] QByteArray qdtoAscii(double d, QLocaleData::DoubleForm form, int precision, bool uppercase); +#if defined(QT_SUPPORTS_INT128) || (defined(Q_CC_MSVC) && (_MSC_VER >= 1930)) +[[nodiscard]] Q_CORE_EXPORT QString quint128toBasicLatin(qinternaluint128 number, + int base = 10); +[[nodiscard]] Q_CORE_EXPORT QString qint128toBasicLatin(qinternalint128 number, + int base = 10); +#endif + [[nodiscard]] constexpr inline bool isZero(double d) { return d == 0; // Amusingly, compilers do not grumble.