[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
This commit is contained in:
Mark de Wever 2022-06-30 17:25:28 +02:00
parent b4466bcd68
commit 3f78683353
7 changed files with 418 additions and 40 deletions

View File

@ -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); }

View File

@ -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

View File

@ -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

View File

@ -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>());
}

View File

@ -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);

View File

@ -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)
{

View File

@ -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 @@ private:
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 @@ private:
return r;
}
static auto fromchars(char const* p, char const* ep, int base = 10)
-> decltype(fromchars(p, ep, base, std::is_signed<X>()))
#ifndef TEST_HAS_NO_INT128
static __int128_t fromchars128_impl(char const* p, char const* ep, int base, true_type)
{
return fromchars(p, ep, base, std::is_signed<X>());
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;
}
char buf[100];
static __uint128_t fromchars128_impl(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_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[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>