forked from OSchip/llvm-project
[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:
parent
b4466bcd68
commit
3f78683353
|
@ -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); }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>());
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue