diff --git a/libcxx/include/__bits b/libcxx/include/__bits --- a/libcxx/include/__bits +++ b/libcxx/include/__bits @@ -43,6 +43,23 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR int __libcpp_clz(unsigned long long __x) _NOEXCEPT { return __builtin_clzll(__x); } +# ifndef _LIBCPP_HAS_NO_INT128 +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR +int __libcpp_clz(__uint128_t __x) _NOEXCEPT { + // The function is written in this form due to C++ constexpr limitations. + // The algorithm: + // - Test whether any bit in the high 64-bits is set + // - No bits set: + // - The high 64-bits contain 64 leading zeros, + // - Add the result of the low 64-bits. + // - Any bits set: + // - The number of leading zeros of the input is the number of leading + // zeros in the high 64-bits. + return ((__x >> 64) == 0) + ? (64 + __builtin_clzll(static_cast(__x))) + : __builtin_clzll(static_cast(__x >> 64)); +} +# endif inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR int __libcpp_popcount(unsigned __x) _NOEXCEPT { return __builtin_popcount(__x); } diff --git a/libcxx/include/__charconv/tables.h b/libcxx/include/__charconv/tables.h --- a/libcxx/include/__charconv/tables.h +++ b/libcxx/include/__charconv/tables.h @@ -35,6 +35,11 @@ static const uint32_t __pow10_32[10]; static const uint64_t __pow10_64[20]; +# ifndef _LIBCPP_HAS_NO_INT128 + // TODO FMT Reduce the number of entries in this table. + static const __uint128_t __pow10_128[40]; + static const int __pow10_128_offset = 0; +# endif static const char __digits_base_10[200]; }; @@ -106,6 +111,51 @@ UINT64_C(1000000000000000000), UINT64_C(10000000000000000000)}; +# ifndef _LIBCPP_HAS_NO_INT128 +template +const __uint128_t __table<_Tp>::__pow10_128[40] = { + UINT64_C(0), + UINT64_C(10), + UINT64_C(100), + UINT64_C(1000), + UINT64_C(10000), + UINT64_C(100000), + UINT64_C(1000000), + UINT64_C(10000000), + UINT64_C(100000000), + UINT64_C(1000000000), + UINT64_C(10000000000), + UINT64_C(100000000000), + UINT64_C(1000000000000), + UINT64_C(10000000000000), + UINT64_C(100000000000000), + UINT64_C(1000000000000000), + UINT64_C(10000000000000000), + UINT64_C(100000000000000000), + UINT64_C(1000000000000000000), + UINT64_C(10000000000000000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100000000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000000000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100000000000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000000000000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(100000000000000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(1000000000000000000), + __uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000000000000), + (__uint128_t(UINT64_C(10000000000000000000)) * UINT64_C(10000000000000000000)) * 10}; +# endif + template const char __table<_Tp>::__digits_base_10[200] = { // clang-format off diff --git a/libcxx/include/__charconv/to_chars_base_10.h b/libcxx/include/__charconv/to_chars_base_10.h --- a/libcxx/include/__charconv/to_chars_base_10.h +++ b/libcxx/include/__charconv/to_chars_base_10.h @@ -14,11 +14,15 @@ #include <__charconv/tables.h> #include <__config> #include +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header #endif +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + _LIBCPP_BEGIN_NAMESPACE_STD #ifndef _LIBCPP_CXX03_LANG @@ -62,7 +66,6 @@ return __itoa::__append8(__itoa::__append1(__first, __value / 100000000), __value % 100000000); } -// This function is used for uint32_t and uint64_t. template _LIBCPP_HIDE_FROM_ABI char* __append10(char* __first, _Tp __value) noexcept { return __itoa::__append8(__itoa::__append2(__first, static_cast(__value / 100000000)), @@ -118,10 +121,65 @@ return __itoa::__append10(__buffer, __value); } +# ifndef _LIBCPP_HAS_NO_INT128 +/// \returns 10^\a exp +/// +/// \pre \a exp [19, 39] +/// +/// \note The lookup table contains a partial set of exponents limiting the +/// range that can be used. However the range is sufficient for +/// \ref __base_10_u128. +_LIBCPP_HIDE_FROM_ABI inline __uint128_t __pow_10(int __exp) noexcept { + _LIBCPP_ASSERT(__exp >= __table<>::__pow10_128_offset, "Index out of bounds"); + return __table<>::__pow10_128[__exp - __table<>::__pow10_128_offset]; +} + +_LIBCPP_HIDE_FROM_ABI inline char* __base_10_u128(char* __buffer, __uint128_t __value) noexcept { + _LIBCPP_ASSERT( + __value > numeric_limits::max(), "The optimizations for this algorithm fail when this isn't true."); + + // Unlike the 64 to 32 bit case the 128 bit case the "upper half" can't be + // stored in the "lower half". Instead we first need to handle the top most + // digits separately. + // + // Maximum unsigned values + // 64 bit 18'446'744'073'709'551'615 (20 digits) + // 128 bit 340'282'366'920'938'463'463'374'607'431'768'211'455 (39 digits) + // step 1 ^ ([0-1] digits) + // step 2 ^^^^^^^^^^^^^^^^^^^^^^^^^ ([0-19] digits) + // step 3 ^^^^^^^^^^^^^^^^^^^^^^^^^ (19 digits) + if (__value >= __itoa::__pow_10(38)) { + // step 1 + __buffer = __itoa::__append1(__buffer, static_cast(__value / __itoa::__pow_10(38))); + __value %= __itoa::__pow_10(38); + + // step 2 always 19 digits. + // They are handled here since leading zeros need to be appended to the buffer, + __buffer = __itoa::__append9(__buffer, static_cast(__value / __itoa::__pow_10(29))); + __value %= __itoa::__pow_10(29); + __buffer = __itoa::__append10(__buffer, static_cast(__value / __itoa::__pow_10(19))); + __value %= __itoa::__pow_10(19); + } + else { + // step 2 + // This version needs to determine the position of the leading non-zero digit. + __buffer = __base_10_u64(__buffer, static_cast(__value / __itoa::__pow_10(19))); + __value %= __itoa::__pow_10(19); + } + + // Step 3 + __buffer = __itoa::__append9(__buffer, static_cast(__value / 10000000000)); + __buffer = __itoa::__append10(__buffer, static_cast(__value % 10000000000)); + + return __buffer; +} +# endif } // namespace __itoa #endif // _LIBCPP_CXX03_LANG _LIBCPP_END_NAMESPACE_STD +_LIBCPP_POP_MACROS + #endif // _LIBCPP___CHARCONV_TO_CHARS_BASE_10_H diff --git a/libcxx/include/charconv b/libcxx/include/charconv --- a/libcxx/include/charconv +++ b/libcxx/include/charconv @@ -117,32 +117,22 @@ namespace __itoa { - template -struct _LIBCPP_HIDDEN __traits_base -{ - using type = uint64_t; - - static _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) - { - auto __t = (64 - std::__libcpp_clz(static_cast(__v | 1))) * 1233 >> 12; - return __t - (__v < __table<>::__pow10_64[__t]) + 1; - } - - static _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v) - { - return __itoa::__base_10_u64(__p, __v); - } - - static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_64)& __pow() { return __table<>::__pow10_64; } -}; +struct _LIBCPP_HIDDEN __traits_base; template -struct _LIBCPP_HIDDEN - __traits_base<_Tp, decltype(void(uint32_t{declval<_Tp>()}))> +struct _LIBCPP_HIDDEN __traits_base<_Tp, __enable_if_t> { using type = uint32_t; + /// The width estimation using a log10 algorithm. + /// + /// The algorithm is based on + /// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + /// Instead of using IntegerLogBase2 it uses __libcpp_clz. Since that + /// function requires its input to have at least one bit set the value of + /// zero is set to one. This means the first element of the lookup table is + /// zero. static _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) { auto __t = (32 - std::__libcpp_clz(static_cast(__v | 1))) * 1233 >> 12; @@ -157,6 +147,61 @@ static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_32)& __pow() { return __table<>::__pow10_32; } }; +template +struct _LIBCPP_HIDDEN + __traits_base<_Tp, __enable_if_t> { + using type = uint64_t; + + /// The width estimation using a log10 algorithm. + /// + /// The algorithm is based on + /// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + /// Instead of using IntegerLogBase2 it uses __libcpp_clz. Since that + /// function requires its input to have at least one bit set the value of + /// zero is set to one. This means the first element of the lookup table is + /// zero. + static _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) { + auto __t = (64 - std::__libcpp_clz(static_cast(__v | 1))) * 1233 >> 12; + return __t - (__v < __table<>::__pow10_64[__t]) + 1; + } + + static _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v) { return __itoa::__base_10_u64(__p, __v); } + + static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_64)& __pow() { return __table<>::__pow10_64; } +}; + + +# ifndef _LIBCPP_HAS_NO_INT128 +template +struct _LIBCPP_HIDDEN + __traits_base<_Tp, __enable_if_t > { + using type = __uint128_t; + + /// The width estimation using a log10 algorithm. + /// + /// The algorithm is based on + /// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + /// Instead of using IntegerLogBase2 it uses __libcpp_clz. Since that + /// function requires its input to have at least one bit set the value of + /// zero is set to one. This means the first element of the lookup table is + /// zero. + static _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) { + _LIBCPP_ASSERT(__v > numeric_limits::max(), "The optimizations for this algorithm fail when this isn't true."); + // There's always a bit set in the upper 64-bits. + auto __t = (128 - std::__libcpp_clz(static_cast(__v >> 64))) * 1233 >> 12; + _LIBCPP_ASSERT(__t >= __table<>::__pow10_128_offset, "Index out of bounds"); + // __t is adjusted since the lookup table misses the lower entries. + return __t - (__v < __table<>::__pow10_128[__t - __table<>::__pow10_128_offset]) + 1; + } + + static _LIBCPP_HIDE_FROM_ABI char* __convert(char* __p, _Tp __v) { return __itoa::__base_10_u128(__p, __v); } + + // TODO FMT This pow function should get an index. + // By moving this to its own header it can be reused by the pow function in to_chars_base_10. + static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_128)& __pow() { return __table<>::__pow10_128; } +}; +#endif + template inline _LIBCPP_HIDE_FROM_ABI bool __mul_overflowed(unsigned char __a, _Tp __b, unsigned char& __r) @@ -271,6 +316,28 @@ return {__last, errc::value_too_large}; } +# ifndef _LIBCPP_HAS_NO_INT128 +template <> +inline _LIBCPP_HIDE_FROM_ABI to_chars_result +__to_chars_itoa(char* __first, char* __last, __uint128_t __value, false_type) +{ + // When the value fits in 64-bits use the 64-bit code path. This reduces + // the number of expensive calculations on 128-bit values. + // + // NOTE the 128-bit code path requires this optimization. + if(__value <= numeric_limits::max()) + return __to_chars_itoa(__first, __last, static_cast(__value), false_type()); + + using __tx = __itoa::__traits<__uint128_t>; + auto __diff = __last - __first; + + if (__tx::digits <= __diff || __tx::__width(__value) <= __diff) + return {__tx::__convert(__first, __value), errc(0)}; + else + return {__last, errc::value_too_large}; +} +#endif + template inline _LIBCPP_HIDE_FROM_ABI to_chars_result __to_chars_integral(char* __first, char* __last, _Tp __value, int __base, @@ -493,7 +560,6 @@ { using _Type = __make_32_64_or_128_bit_t<_Tp>; static_assert(!is_same<_Type, void>::value, "unsupported integral type used in to_chars"); - static_assert(sizeof(_Tp) <= sizeof(int64_t), "128-bit integral support isn't available yet in to_chars"); return std::__to_chars_itoa(__first, __last, static_cast<_Type>(__value), is_signed<_Tp>()); } @@ -504,7 +570,6 @@ _LIBCPP_ASSERT(2 <= __base && __base <= 36, "base not in [2, 36]"); using _Type = __make_32_64_or_128_bit_t<_Tp>; - static_assert(sizeof(_Tp) <= sizeof(int64_t), "128-bit integral support isn't available yet in to_chars"); return std::__to_chars_integral(__first, __last, static_cast<_Type>(__value), __base, is_signed<_Tp>()); } diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp --- a/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp +++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp @@ -40,7 +40,8 @@ } { - char s[] = "0X7BAtSGHDkEIXZg "; + // The string has more characters than valid in an 128-bit value. + char s[] = "0X7BAtSGHDkEIXZgQRfYChLpOzRnM "; // The letters from a (or A) through z (or Z) are ascribed the // values 10 through 35; (C11 7.22.1.4/3) @@ -88,13 +89,15 @@ void operator()() { std::from_chars_result r; - T x; + T x = 42; { // If the pattern allows for an optional sign, // but the string has no digit characters following the sign, char s[] = "- 9+12"; r = std::from_chars(s, s + sizeof(s), x); + // value is unmodified, + assert(x == 42); // no characters match the pattern. assert(r.ptr == s); assert(r.ec == std::errc::invalid_argument); diff --git a/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp --- a/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp +++ b/libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp @@ -19,6 +19,33 @@ #include "test_macros.h" #include "charconv_test_helpers.h" +#ifndef TEST_HAS_NO_INT128 +__uint128_t make_u128(__uint128_t a, uint64_t b) { + a *= 1000000000000000000UL; + a *= 10; + return a + b; +} + +__uint128_t make_u128(__uint128_t a, uint64_t b, uint64_t c) { + a *= 10000000000000ULL; + a += b; + a *= 10000000000000ULL; + return a + c; +} + +__int128_t make_i128(__int128_t a, int64_t b) { + if (a < 0) + return -make_u128(-a, b); + return make_u128(a, b); +} + +__int128_t make_i128(__int128_t a, __int128_t b, int64_t c) { + if (a < 0) + return -make_u128(-a, b, c); + return make_u128(a, b, c); +} +#endif + template struct test_basics : to_chars_test_base { @@ -61,6 +88,27 @@ test(123456789012345678UL, "123456789012345678"); test(1234567890123456789UL, "1234567890123456789"); test(12345678901234567890UL, "12345678901234567890"); +#ifndef TEST_HAS_NO_INT128 + test(make_u128(12UL, 3456789012345678901UL), "123456789012345678901"); + test(make_u128(123UL, 4567890123456789012UL), "1234567890123456789012"); + test(make_u128(1234UL, 5678901234567890123UL), "12345678901234567890123"); + test(make_u128(12345UL, 6789012345678901234UL), "123456789012345678901234"); + test(make_u128(123456UL, 7890123456789012345UL), "1234567890123456789012345"); + test(make_u128(1234567UL, 8901234567890123456UL), "12345678901234567890123456"); + test(make_u128(12345678UL, 9012345678901234567UL), "123456789012345678901234567"); + test(make_u128(123456789UL, 123456789012345678UL), "1234567890123456789012345678"); + test(make_u128(123UL, 4567890123456UL, 7890123456789UL), "12345678901234567890123456789"); + test(make_u128(1234UL, 5678901234567UL, 8901234567890UL), "123456789012345678901234567890"); + test(make_u128(12345UL, 6789012345678UL, 9012345678901UL), "1234567890123456789012345678901"); + test(make_u128(123456UL, 7890123456789UL, 123456789012UL), "12345678901234567890123456789012"); + test(make_u128(1234567UL, 8901234567890UL, 1234567890123UL), "123456789012345678901234567890123"); + test(make_u128(12345678UL, 9012345678901UL, 2345678901234UL), "1234567890123456789012345678901234"); + test(make_u128(123456789UL, 123456789012UL, 3456789012345UL), "12345678901234567890123456789012345"); + test(make_u128(1234567890UL, 1234567890123UL, 4567890123456UL), "123456789012345678901234567890123456"); + test(make_u128(12345678901UL, 2345678901234UL, 5678901234567UL), "1234567890123456789012345678901234567"); + test(make_u128(123456789012UL, 3456789012345UL, 6789012345678UL), "12345678901234567890123456789012345678"); + test(make_u128(1234567890123UL, 4567890123456UL, 7890123456789UL), "123456789012345678901234567890123456789"); +#endif // Test special cases with zeros inside a value string representation, // to_chars algorithm processes them in a special way and should not @@ -86,6 +134,27 @@ test(100000000000000000UL, "100000000000000000"); test(1000000000000000000UL, "1000000000000000000"); test(10000000000000000000UL, "10000000000000000000"); +#ifndef TEST_HAS_NO_INT128 + test(make_u128(10UL, 0), "100000000000000000000"); + test(make_u128(100UL, 0), "1000000000000000000000"); + test(make_u128(1000UL, 0), "10000000000000000000000"); + test(make_u128(10000UL, 0), "100000000000000000000000"); + test(make_u128(100000UL, 0), "1000000000000000000000000"); + test(make_u128(1000000UL, 0), "10000000000000000000000000"); + test(make_u128(10000000UL, 0), "100000000000000000000000000"); + test(make_u128(100000000UL, 0), "1000000000000000000000000000"); + test(make_u128(100UL, 0, 0), "10000000000000000000000000000"); + test(make_u128(1000UL, 0, 0), "100000000000000000000000000000"); + test(make_u128(10000UL, 0, 0), "1000000000000000000000000000000"); + test(make_u128(100000UL, 0, 0), "10000000000000000000000000000000"); + test(make_u128(1000000UL, 0, 0), "100000000000000000000000000000000"); + test(make_u128(10000000UL, 0, 0), "1000000000000000000000000000000000"); + test(make_u128(100000000UL, 0, 0), "10000000000000000000000000000000000"); + test(make_u128(1000000000UL, 0, 0), "100000000000000000000000000000000000"); + test(make_u128(10000000000UL, 0, 0), "1000000000000000000000000000000000000"); + test(make_u128(100000000000UL, 0, 0), "10000000000000000000000000000000000000"); + test(make_u128(1000000000000UL, 0, 0), "100000000000000000000000000000000000000"); +#endif for (int b = 2; b < 37; ++b) { @@ -138,6 +207,29 @@ test(-12345678901234567L, "-12345678901234567"); test(-123456789012345678L, "-123456789012345678"); test(-1234567890123456789L, "-1234567890123456789"); +#ifndef TEST_HAS_NO_INT128 + test(make_i128(-1L, 2345678901234567890L), "-12345678901234567890"); + test(make_i128(-12L, 3456789012345678901L), "-123456789012345678901"); + test(make_i128(-123L, 4567890123456789012L), "-1234567890123456789012"); + test(make_i128(-1234L, 5678901234567890123L), "-12345678901234567890123"); + test(make_i128(-12345L, 6789012345678901234L), "-123456789012345678901234"); + test(make_i128(-123456L, 7890123456789012345L), "-1234567890123456789012345"); + test(make_i128(-1234567L, 8901234567890123456L), "-12345678901234567890123456"); + test(make_i128(-12345678L, 9012345678901234567L), "-123456789012345678901234567"); + test(make_i128(-123456789L, 123456789012345678L), "-1234567890123456789012345678"); + test(make_i128(-1234567890L, 1234567890123456789L), "-12345678901234567890123456789"); + test(make_i128(-123L, 4567890123456L, 7890123456789L), "-12345678901234567890123456789"); + test(make_i128(-1234L, 5678901234567L, 8901234567890L), "-123456789012345678901234567890"); + test(make_i128(-12345L, 6789012345678L, 9012345678901L), "-1234567890123456789012345678901"); + test(make_i128(-123456L, 7890123456789L, 123456789012L), "-12345678901234567890123456789012"); + test(make_i128(-1234567L, 8901234567890L, 1234567890123L), "-123456789012345678901234567890123"); + test(make_i128(-12345678L, 9012345678901L, 2345678901234L), "-1234567890123456789012345678901234"); + test(make_i128(-123456789L, 123456789012L, 3456789012345L), "-12345678901234567890123456789012345"); + test(make_i128(-1234567890L, 1234567890123L, 4567890123456L), "-123456789012345678901234567890123456"); + test(make_i128(-12345678901L, 2345678901234L, 5678901234567L), "-1234567890123456789012345678901234567"); + test(make_i128(-123456789012L, 3456789012345L, 6789012345678L), "-12345678901234567890123456789012345678"); + test(make_i128(-1234567890123L, 4567890123456L, 7890123456789L), "-123456789012345678901234567890123456789"); +#endif // Test special cases with zeros inside a value string representation, // to_chars algorithm processes them in a special way and should not @@ -161,6 +253,29 @@ test(-10000000000000000L, "-10000000000000000"); test(-100000000000000000L, "-100000000000000000"); test(-1000000000000000000L, "-1000000000000000000"); +#ifndef TEST_HAS_NO_INT128 + test(make_i128(-1L, 0L), "-10000000000000000000"); + test(make_i128(-10L, 0L), "-100000000000000000000"); + test(make_i128(-100L, 0L), "-1000000000000000000000"); + test(make_i128(-1000L, 0L), "-10000000000000000000000"); + test(make_i128(-10000L, 0L), "-100000000000000000000000"); + test(make_i128(-100000L, 0L), "-1000000000000000000000000"); + test(make_i128(-1000000L, 0L), "-10000000000000000000000000"); + test(make_i128(-10000000L, 0L), "-100000000000000000000000000"); + test(make_i128(-100000000L, 0L), "-1000000000000000000000000000"); + test(make_i128(-1000000000L, 0L), "-10000000000000000000000000000"); + test(make_i128(-100L, 0L, 0L), "-10000000000000000000000000000"); + test(make_i128(-1000L, 0L, 0L), "-100000000000000000000000000000"); + test(make_i128(-10000L, 0L, 0L), "-1000000000000000000000000000000"); + test(make_i128(-100000L, 0L, 0L), "-10000000000000000000000000000000"); + test(make_i128(-1000000L, 0L, 0L), "-100000000000000000000000000000000"); + test(make_i128(-10000000L, 0L, 0L), "-1000000000000000000000000000000000"); + test(make_i128(-100000000L, 0L, 0L), "-10000000000000000000000000000000000"); + test(make_i128(-1000000000L, 0L, 0L), "-100000000000000000000000000000000000"); + test(make_i128(-10000000000L, 0L, 0L), "-1000000000000000000000000000000000000"); + test(make_i128(-100000000000L, 0L, 0L), "-10000000000000000000000000000000000000"); + test(make_i128(-1000000000000L, 0L, 0L), "-100000000000000000000000000000000000000"); +#endif for (int b = 2; b < 37; ++b) { diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h --- a/libcxx/test/support/charconv_test_helpers.h +++ b/libcxx/test/support/charconv_test_helpers.h @@ -71,8 +71,7 @@ constexpr bool fits_in(T v) { - return _fits_in(v, is_non_narrowing(v), std::is_signed(), - std::is_signed()); + return _fits_in(v, is_non_narrowing(v), std::is_signed(), std::is_signed()); } template @@ -115,8 +114,16 @@ assert(buf[i] == static_cast(i + 1)); *r.ptr = '\0'; - auto a = fromchars(buf, r.ptr, args...); - assert(v == a); +#ifndef TEST_HAS_NO_INT128 + if (sizeof(X) == sizeof(__int128_t)) { + auto a = fromchars128_impl(buf, r.ptr, args...); + assert(v == a); + } else +#endif + { + auto a = fromchars_impl(buf, r.ptr, args...); + assert(v == a); + } auto ep = r.ptr - 1; r = to_chars(buf, ep, v, args...); @@ -125,7 +132,7 @@ } private: - static long long fromchars(char const* p, char const* ep, int base, true_type) + static long long fromchars_impl(char const* p, char const* ep, int base, true_type) { char* last; auto r = strtoll(p, &last, base); @@ -134,7 +141,7 @@ return r; } - static unsigned long long fromchars(char const* p, char const* ep, int base, false_type) + static unsigned long long fromchars_impl(char const* p, char const* ep, int base, false_type) { char* last; auto r = strtoull(p, &last, base); @@ -142,14 +149,57 @@ return r; } +#ifndef TEST_HAS_NO_INT128 + static __int128_t fromchars128_impl(char const* p, char const* ep, int base, true_type) + { + char* last; + __int128_t r = strtoll(p, &last, base); + if(errno != ERANGE) { + assert(last == ep); + return r; + } + + // When the value doesn't fit in a long long use from_chars. This is + // not ideal since it does a round-trip test instead if using an + // external source. + std::from_chars_result s = std::from_chars(p, ep, r, base); + assert(s.ec == std::errc{}); + assert(s.ptr == ep); - static auto fromchars(char const* p, char const* ep, int base = 10) - -> decltype(fromchars(p, ep, base, std::is_signed())) + return r; + } + + static __uint128_t fromchars128_impl(char const* p, char const* ep, int base, false_type) { - return fromchars(p, ep, base, std::is_signed()); + char* last; + __uint128_t r = strtoull(p, &last, base); + if(errno != ERANGE) { + assert(last == ep); + return r; + } + + std::from_chars_result s = std::from_chars(p, ep, r, base); + assert(s.ec == std::errc{}); + assert(s.ptr == ep); + + return r; + } + + static auto fromchars128_impl(char const* p, char const* ep, int base = 10) + -> decltype(fromchars128_impl(p, ep, base, std::is_signed())) + { + return fromchars128_impl(p, ep, base, std::is_signed()); + } + +#endif + + static auto fromchars_impl(char const* p, char const* ep, int base = 10) + -> decltype(fromchars_impl(p, ep, base, std::is_signed())) + { + return fromchars_impl(p, ep, base, std::is_signed()); } - char buf[100]; + char buf[150]; }; template @@ -201,7 +251,7 @@ } private: - char buf[100]; + char buf[150]; }; template @@ -227,9 +277,29 @@ return {}; } -auto all_signed = type_list(); -auto all_unsigned = type_list(); +auto all_signed = type_list< + char, + signed char, + short, + int, + long, + long long +#ifndef TEST_HAS_NO_INT128 + , + __int128_t +#endif + >(); +auto all_unsigned = type_list< + unsigned char, + unsigned short, + unsigned int, + unsigned long, + unsigned long long +#ifndef TEST_HAS_NO_INT128 + , + __uint128_t +#endif + >(); auto integrals = concat(all_signed, all_unsigned); template