[libcxx-commits] [libcxx] 3f78683 - [libc++] Implements 128-bit support in to_chars.
Mark de Wever via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Jul 7 08:32:33 PDT 2022
Author: Mark de Wever
Date: 2022-07-07T17:32:27+02:00
New Revision: 3f78683353e272224e9bcc223f7ba05ab374a8c2
URL: https://github.com/llvm/llvm-project/commit/3f78683353e272224e9bcc223f7ba05ab374a8c2
DIFF: https://github.com/llvm/llvm-project/commit/3f78683353e272224e9bcc223f7ba05ab374a8c2.diff
LOG: [libc++] Implements 128-bit support in to_chars.
This is required by the Standard and makes it possible to add full
128-bit support to format.
The patch also fixes 128-bit from_chars "support". One unit test
required a too large value, this failed on 128-bit; the fix was to add
more characters to the input.
Note only base 10 has been optimized. Other bases can be optimized.
Note the 128-bit lookup table could be made smaller. This will be done later. I
really want to get 128-bit working in to_chars and format in the upcomming
LLVM 15 release, these optimizations aren't critical.
Reviewed By: #libc, ldionne
Differential Revision: https://reviews.llvm.org/D128929
Added:
Modified:
libcxx/include/__bits
libcxx/include/__charconv/tables.h
libcxx/include/__charconv/to_chars_base_10.h
libcxx/include/charconv
libcxx/test/std/utilities/charconv/charconv.from.chars/integral.pass.cpp
libcxx/test/std/utilities/charconv/charconv.to.chars/integral.pass.cpp
libcxx/test/support/charconv_test_helpers.h
Removed:
################################################################################
diff --git a/libcxx/include/__bits b/libcxx/include/__bits
index 1eee8f576e9e1..92ef5c0a7b492 100644
--- a/libcxx/include/__bits
+++ b/libcxx/include/__bits
@@ -43,6 +43,23 @@ int __libcpp_clz(unsigned long __x) _NOEXCEPT { return __builtin_clzl(__x);
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<unsigned long long>(__x)))
+ : __builtin_clzll(static_cast<unsigned long long>(__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
index a2f7f7ce29b37..83f39e6fd2efd 100644
--- a/libcxx/include/__charconv/tables.h
+++ b/libcxx/include/__charconv/tables.h
@@ -35,6 +35,11 @@ struct __table {
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 @@ const uint64_t __table<_Tp>::__pow10_64[20] = {UINT64_C(0),
UINT64_C(1000000000000000000),
UINT64_C(10000000000000000000)};
+# ifndef _LIBCPP_HAS_NO_INT128
+template <class _Tp>
+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 <class _Tp>
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
index 91c209559aff9..d25deffc592fc 100644
--- 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 <cstdint>
+#include <limits>
#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 @@ _LIBCPP_HIDE_FROM_ABI inline char* __append9(char* __first, uint32_t __value) no
return __itoa::__append8(__itoa::__append1(__first, __value / 100000000), __value % 100000000);
}
-// This function is used for uint32_t and uint64_t.
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI char* __append10(char* __first, _Tp __value) noexcept {
return __itoa::__append8(__itoa::__append2(__first, static_cast<uint32_t>(__value / 100000000)),
@@ -118,10 +121,65 @@ _LIBCPP_HIDE_FROM_ABI inline char* __base_10_u64(char* __buffer, uint64_t __valu
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<uint64_t>::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<uint32_t>(__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<uint32_t>(__value / __itoa::__pow_10(29)));
+ __value %= __itoa::__pow_10(29);
+ __buffer = __itoa::__append10(__buffer, static_cast<uint64_t>(__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<uint64_t>(__value / __itoa::__pow_10(19)));
+ __value %= __itoa::__pow_10(19);
+ }
+
+ // Step 3
+ __buffer = __itoa::__append9(__buffer, static_cast<uint32_t>(__value / 10000000000));
+ __buffer = __itoa::__append10(__buffer, static_cast<uint64_t>(__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
index 6a63e5fe9057b..c3db69a39e4b5 100644
--- a/libcxx/include/charconv
+++ b/libcxx/include/charconv
@@ -117,32 +117,22 @@ from_chars_result from_chars(const char*, const char*, bool, int = 10) = delete;
namespace __itoa
{
-
template <typename _Tp, typename = void>
-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<type>(__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 <typename _Tp>
-struct _LIBCPP_HIDDEN
- __traits_base<_Tp, decltype(void(uint32_t{declval<_Tp>()}))>
+struct _LIBCPP_HIDDEN __traits_base<_Tp, __enable_if_t<sizeof(_Tp) <= sizeof(uint32_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<type>(__v | 1))) * 1233 >> 12;
@@ -157,6 +147,61 @@ struct _LIBCPP_HIDDEN
static _LIBCPP_HIDE_FROM_ABI decltype(__table<>::__pow10_32)& __pow() { return __table<>::__pow10_32; }
};
+template <typename _Tp>
+struct _LIBCPP_HIDDEN
+ __traits_base<_Tp, __enable_if_t<sizeof(_Tp) == sizeof(uint64_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<type>(__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 <typename _Tp>
+struct _LIBCPP_HIDDEN
+ __traits_base<_Tp, __enable_if_t<sizeof(_Tp) == sizeof(__uint128_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<uint64_t>::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<uint64_t>(__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 <typename _Tp>
inline _LIBCPP_HIDE_FROM_ABI bool
__mul_overflowed(unsigned char __a, _Tp __b, unsigned char& __r)
@@ -271,6 +316,28 @@ __to_chars_itoa(char* __first, char* __last, _Tp __value, false_type)
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<uint64_t>::max())
+ return __to_chars_itoa(__first, __last, static_cast<uint64_t>(__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 <typename _Tp>
inline _LIBCPP_HIDE_FROM_ABI to_chars_result
__to_chars_integral(char* __first, char* __last, _Tp __value, int __base,
@@ -493,7 +560,6 @@ to_chars(char* __first, char* __last, _Tp __value)
{
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 @@ to_chars(char* __first, char* __last, _Tp __value, int __base)
_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
index 0c10c0dfb5bca..fd47cb4bbb6cf 100644
--- 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 @@ struct test_basics
}
{
- 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 @@ struct test_signed
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
index 70f85a537568a..db52ac9fd3144 100644
--- 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 <typename T>
struct test_basics : to_chars_test_base<T>
{
@@ -61,6 +88,27 @@ struct test_basics : to_chars_test_base<T>
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 @@ struct test_basics : to_chars_test_base<T>
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 @@ struct test_signed : to_chars_test_base<T>
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 @@ struct test_signed : to_chars_test_base<T>
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
index 32a8d48b4bfd3..c8d104510f26f 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -71,8 +71,7 @@ template <typename X, typename T>
constexpr bool
fits_in(T v)
{
- return _fits_in<X>(v, is_non_narrowing<X>(v), std::is_signed<T>(),
- std::is_signed<X>());
+ return _fits_in<X>(v, is_non_narrowing<X>(v), std::is_signed<T>(), std::is_signed<X>());
}
template <typename X>
@@ -115,8 +114,16 @@ struct to_chars_test_base
assert(buf[i] == static_cast<char>(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 @@ struct to_chars_test_base
}
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 @@ struct to_chars_test_base
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 @@ struct to_chars_test_base
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<X>()))
+ 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<X>());
+ 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<X>()))
+ {
+ return fromchars128_impl(p, ep, base, std::is_signed<X>());
+ }
+
+#endif
+
+ static auto fromchars_impl(char const* p, char const* ep, int base = 10)
+ -> decltype(fromchars_impl(p, ep, base, std::is_signed<X>()))
+ {
+ return fromchars_impl(p, ep, base, std::is_signed<X>());
}
- char buf[100];
+ char buf[150];
};
template <typename X>
@@ -201,7 +251,7 @@ struct roundtrip_test_base
}
private:
- char buf[100];
+ char buf[150];
};
template <typename... T>
@@ -227,9 +277,29 @@ constexpr auto concat(L1, L2) -> concat_t<L1, L2>
return {};
}
-auto all_signed = type_list<char, signed char, short, int, long, long long>();
-auto all_unsigned = type_list<unsigned char, unsigned short, unsigned int,
- unsigned long, unsigned long long>();
+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 <template <typename> class Fn, typename... Ts>
More information about the libcxx-commits
mailing list