diff --git a/libcxx/include/__bits b/libcxx/include/__bits --- a/libcxx/include/__bits +++ b/libcxx/include/__bits @@ -43,6 +43,22 @@ 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 { +#if _LIBCPP_STD_VER > 11 + unsigned long long __hi = __x >> 64; + if (__hi == 0) + return 64 + __builtin_clzll(static_cast(__x)); + return __builtin_clzll(__hi); +#else + // The same as above but constexpr in C++11. + return ((__x >> 64) == 0) + ? (64 + __builtin_clzll(static_cast(__x))) + : __builtin_clzll(static_cast(__x >> 64)); +#endif +} +#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,53 @@ UINT64_C(1000000000000000000), UINT64_C(10000000000000000000)}; +# ifndef _LIBCPP_HAS_NO_INT128 +_LIBCPP_CONSTEVAL __uint128_t __uint128_c(__uint128_t __a, __uint128_t __b) { return __a * __b; } + +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_c(UINT64_C(10000000000000000000), UINT64_C(10)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(100)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(1000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(10000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(100000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(1000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(10000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(100000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(1000000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(10000000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(100000000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(1000000000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(10000000000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(100000000000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(1000000000000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(10000000000000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(100000000000000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(1000000000000000000)), + __uint128_c(UINT64_C(10000000000000000000), UINT64_C(10000000000000000000)), + __uint128_c(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 @@ -58,8 +62,12 @@ return __itoa::__append6(__itoa::__append2(__first, __value / 1000000), __value % 1000000); } -_LIBCPP_HIDE_FROM_ABI inline char* __append9(char* __first, uint32_t __value) noexcept { - 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 inline char* __append9(char* __first, _Tp __value) noexcept { + return __itoa::__append8( + __itoa::__append1(__first, static_cast(__value / 100000000)), + static_cast(__value % 100000000)); } // This function is used for uint32_t and uint64_t. @@ -118,10 +126,71 @@ 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 constexpr __uint128_t __pow_10(int __exp) noexcept { +#if _LIBCPP_STD_VER > 11 + // Disabled to be constexpr in C++11. + _LIBCPP_ASSERT(__exp >= __table<>::__pow10_128_offset, "Index out of bounds"); +#endif + 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 8'446'744'073'709'551'615 (19 digits) + // 128 bit 340'282'366'920'938'463'463'374'607'431'768'211'455 39 digits) + // step 1 ^^ ([0-2] digits) + // step 2 ^^^^^^^^^^^^^^^^^^^^^^^^ ([0-18] digits) + // step 3 ^^^^^^^^^^^^^^^^^^^^^^^^^ (19 digits) + + if (__value >= __itoa::__pow_10(37)) { + // step 1 + if (__value >= __itoa::__pow_10(38)) + __buffer = __itoa::__append2(__buffer, static_cast(__value / __itoa::__pow_10(37))); + else + __buffer = __itoa::__append1(__buffer, static_cast(__value / __itoa::__pow_10(37))); + __value %= __itoa::__pow_10(37); + + // step 2 always 18 digits. + // They are handled here since leading zeros need to be appended to the buffer, + __buffer = __itoa::__append8(__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,29 +117,11 @@ 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; @@ -157,6 +139,45 @@ 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; + + 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; + + 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 +292,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, @@ -418,7 +461,7 @@ typename enable_if<(sizeof(_Tp) >= sizeof(unsigned)), int>::type = 0> _LIBCPP_HIDE_FROM_ABI to_chars_result __to_chars_integral(char* __first, char* __last, _Tp __value) { - return __itoa::__integral<_Base>::__to_chars(__first, __last, __value); + return __itoa::__integral<_Base>::__to_chars(__first, __last, __value); // XXX cast to uint64_t? } template ; 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 +546,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,25 @@ test(123456789012345678UL, "123456789012345678"); test(1234567890123456789UL, "1234567890123456789"); test(12345678901234567890UL, "12345678901234567890"); + 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"); // Test special cases with zeros inside a value string representation, // to_chars algorithm processes them in a special way and should not @@ -86,6 +132,25 @@ test(100000000000000000UL, "100000000000000000"); test(1000000000000000000UL, "1000000000000000000"); test(10000000000000000000UL, "10000000000000000000"); + 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"); for (int b = 2; b < 37; ++b) { @@ -138,6 +203,27 @@ test(-12345678901234567L, "-12345678901234567"); test(-123456789012345678L, "-123456789012345678"); test(-1234567890123456789L, "-1234567890123456789"); + 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"); // Test special cases with zeros inside a value string representation, // to_chars algorithm processes them in a special way and should not @@ -161,6 +247,27 @@ test(-10000000000000000L, "-10000000000000000"); test(-100000000000000000L, "-100000000000000000"); test(-1000000000000000000L, "-1000000000000000000"); + 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"); 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(buf, r.ptr, args...); + assert(v == a); + } else +#endif + { + auto a = fromchars(buf, r.ptr, args...); + assert(v == a); + } auto ep = r.ptr - 1; r = to_chars(buf, ep, v, args...); @@ -142,6 +149,49 @@ return r; } +#ifndef TEST_HAS_NO_INT128 + static __int128_t fromchars128(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); + + return r; + } + + static __uint128_t fromchars128(char const* p, char const* ep, int base, false_type) + { + 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(char const* p, char const* ep, int base = 10) + -> decltype(fromchars128(p, ep, base, std::is_signed())) + { + return fromchars128(p, ep, base, std::is_signed()); + } + +#endif static auto fromchars(char const* p, char const* ep, int base = 10) -> decltype(fromchars(p, ep, base, std::is_signed())) @@ -149,7 +199,7 @@ return fromchars(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