Skip to content

Commit

Permalink
Merge pull request #400 from ckormanyos/from_chars
Browse files Browse the repository at this point in the history
Fixes #398 via impl and test from_chars()
  • Loading branch information
ckormanyos authored Jan 22, 2024
2 parents df1fa53 + 7183be8 commit 296f2c0
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 32 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,14 @@ in the wide-integer project.
- All wide-integer-types are move constructable.
- All wide-integer types having the same widths and having the same limb-type (but possibly different sign) are move-assignable and `std::move()`-capable.

### Importing and exporting bits
### Importing and exporting characters and bits

For sufficiently modern standards-conforming compilers,
namespace-specific functions `to_chars()` and `from_chars()`
are available. These each have the _usual_ `<charconv>`-like
behavior and can also be used in `constexpr`-context.
For motivational words on these, see also
[issue 398](https://github.com/ckormanyos/wide-integer/issues/398).

Support for importing and exporting bits is granted by the subroutines
`import_bits()` and `export_bits()`. Their interfaces, input/output forms
Expand Down
137 changes: 113 additions & 24 deletions math/wide_integer/uintwide_t.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////
// Copyright Christopher Kormanyos 1999 - 2023. //
// Copyright Christopher Kormanyos 1999 - 2024. //
// Distributed under the Boost Software License, //
// Version 1.0. (See accompanying file LICENSE_1_0.txt //
// or copy at http://www.boost.org/LICENSE_1_0.txt) //
Expand Down Expand Up @@ -1617,10 +1617,21 @@
typename LimbType,
typename AllocatorType,
const bool IsSigned>
WIDE_INTEGER_CONSTEXPR
auto to_chars(char* first,
char* last,
const uintwide_t<Width2, LimbType, AllocatorType, IsSigned>& x,
int base = static_cast<int>(INT8_C(10))) -> std::to_chars_result;

template<const size_t Width2,
typename LimbType,
typename AllocatorType,
const bool IsSigned>
WIDE_INTEGER_CONSTEXPR
auto from_chars(const char* first,
const char* last,
uintwide_t<Width2, LimbType, AllocatorType, IsSigned>& x,
int base = static_cast<int>(INT8_C(10))) -> std::from_chars_result;
#endif

#if !defined(WIDE_INTEGER_DISABLE_TO_STRING)
Expand Down Expand Up @@ -2454,7 +2465,7 @@
typename representation_type::allocator_type()
}
{
if(!rd_string(str_input))
if(!rd_string(str_input, (std::numeric_limits<unsigned_fast_type>::max)(), 0))
{
static_cast<void>(operator=((std::numeric_limits<uintwide_t>::max)()));
}
Expand Down Expand Up @@ -3549,6 +3560,26 @@
#endif
friend auto ::test_uintwide_t_edge::test_various_isolated_edge_cases() -> bool;

template<const size_t OtherWidth2,
typename OtherLimbType,
typename OtherAllocatorType,
const bool OtherIsSignedLeft,
const bool OtherIsSignedRight>
friend WIDE_INTEGER_CONSTEXPR auto divmod(const uintwide_t<OtherWidth2, OtherLimbType, OtherAllocatorType, OtherIsSignedLeft >& a, // NOLINT(readability-redundant-declaration)
const uintwide_t<OtherWidth2, OtherLimbType, OtherAllocatorType, OtherIsSignedRight>& b,
std::enable_if_t<((!OtherIsSignedLeft) && (!OtherIsSignedRight)), int>* p_nullparam) -> std::pair<uintwide_t<OtherWidth2, OtherLimbType, OtherAllocatorType, OtherIsSignedLeft>, uintwide_t<OtherWidth2, OtherLimbType, OtherAllocatorType, OtherIsSignedRight>>;

#if (defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L))
template<const size_t OtherWidth2,
typename OtherLimbType,
typename OtherAllocatorType,
const bool OtherIsSigned>
friend WIDE_INTEGER_CONSTEXPR auto from_chars(const char* first, // NOLINT(readability-redundant-declaration)
const char* last,
uintwide_t<OtherWidth2, OtherLimbType, OtherAllocatorType, OtherIsSigned>& x,
int base) -> std::from_chars_result;
#endif

template<const size_t OtherWidth2,
typename OtherLimbType,
typename OtherAllocatorType,
Expand Down Expand Up @@ -5331,13 +5362,15 @@
}

// Read string function.
WIDE_INTEGER_CONSTEXPR auto rd_string(const char* str_input) -> bool // NOLINT(readability-function-cognitive-complexity)
WIDE_INTEGER_CONSTEXPR auto rd_string(const char* str_input, const unsigned_fast_type count, const int base_hint) -> bool // NOLINT(readability-function-cognitive-complexity,bugprone-easily-swappable-parameters)
{
detail::fill_unsafe(values.begin(), values.end(), static_cast<limb_type>(UINT8_C(0)));

const auto str_length = detail::strlen_unsafe(str_input);

auto base = static_cast<std::uint_fast8_t>(UINT8_C(10));
const auto str_length =
static_cast<unsigned_fast_type>
(
(count == (std::numeric_limits<unsigned_fast_type>::max)()) ? detail::strlen_unsafe(str_input) : count
);

auto pos = static_cast<unsigned_fast_type>(UINT8_C(0));

Expand All @@ -5359,33 +5392,41 @@
++pos;
}

// Perform a dynamic detection of the base.
if(str_length > static_cast<unsigned_fast_type>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(0))))
// Set the base if the client has supplied a non-zero base-hint.
auto base = static_cast<std::uint_fast8_t>(base_hint);

if(base == static_cast<std::uint_fast8_t>(UINT8_C(0)))
{
const auto might_be_oct_or_hex = ((str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(0)))] == '0') && (str_length > static_cast<unsigned_fast_type>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(0))))); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
base = static_cast<std::uint_fast8_t>(UINT8_C(10));

if(might_be_oct_or_hex)
// Perform a dynamic detection of the base.
if(str_length > static_cast<unsigned_fast_type>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(0))))
{
if((str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(1)))] >= '0') && (str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(1)))] <= '8')) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
const auto might_be_oct_or_hex = ((str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(0)))] == '0') && (str_length > static_cast<unsigned_fast_type>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(0))))); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)

if(might_be_oct_or_hex)
{
// The input format is octal.
base = static_cast<std::uint_fast8_t>(UINT8_C(8));
if((str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(1)))] >= '0') && (str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(1)))] <= '8')) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
{
// The input format is octal.
base = static_cast<std::uint_fast8_t>(UINT8_C(8));

pos = static_cast<unsigned_fast_type>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(1)));
pos = static_cast<unsigned_fast_type>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(1)));
}
else if((str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(1)))] == 'x') || (str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(1)))] == 'X')) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
{
// The input format is hexadecimal.
base = static_cast<std::uint_fast8_t>(UINT8_C(16));

pos = static_cast<unsigned_fast_type>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(2)));
}
}
else if((str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(1)))] == 'x') || (str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(1)))] == 'X')) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
else if((str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(0)))] >= '0') && (str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(0)))] <= '9')) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
{
// The input format is hexadecimal.
base = static_cast<std::uint_fast8_t>(UINT8_C(16));

pos = static_cast<unsigned_fast_type>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(2)));
// The input format is decimal.
;
}
}
else if((str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(0)))] >= '0') && (str_input[static_cast<std::size_t>(static_cast<std::size_t>(pos) + static_cast<std::size_t>(UINT8_C(0)))] <= '9')) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
{
// The input format is decimal.
;
}
}

auto char_is_valid = true;
Expand Down Expand Up @@ -7283,6 +7324,7 @@
typename LimbType,
typename AllocatorType,
const bool IsSigned>
WIDE_INTEGER_CONSTEXPR
auto to_chars(char* first,
char* last,
const uintwide_t<Width2, LimbType, AllocatorType, IsSigned>& x,
Expand Down Expand Up @@ -7441,6 +7483,53 @@

return result;
}

template<const size_t Width2,
typename LimbType,
typename AllocatorType,
const bool IsSigned>
WIDE_INTEGER_CONSTEXPR
auto from_chars(const char* first,
const char* last,
uintwide_t<Width2, LimbType, AllocatorType, IsSigned>& x,
int base) -> std::from_chars_result
{
using local_wide_integer_type = uintwide_t<Width2, LimbType, AllocatorType, IsSigned>;

using local_wide_integer_type = uintwide_t<Width2, LimbType, AllocatorType, IsSigned>;
using local_limb_type = typename local_wide_integer_type::limb_type;

detail::fill_unsafe(x.values.begin(), x.values.end(), static_cast<local_limb_type>(UINT8_C(0)));

const auto str_len = static_cast<unsigned_fast_type>(detail::distance_unsafe(first, last));

const auto result_rd_string_is_ok = x.rd_string(first, str_len, base);

std::from_chars_result result { };

if(result_rd_string_is_ok)
{
result =
std::from_chars_result
{
last,
std::errc()
};
}
else
{
result =
std::from_chars_result
{
last,
std::errc::result_out_of_range
};

detail::fill_unsafe(x.values.begin(), x.values.end(), static_cast<local_limb_type>(UINT8_C(0)));
}

return result;
}
#endif

#if !defined(WIDE_INTEGER_DISABLE_TO_STRING)
Expand Down
5 changes: 3 additions & 2 deletions test/test.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////
// Copyright Christopher Kormanyos 2018 - 2023. //
// Copyright Christopher Kormanyos 2018 - 2024. //
// Distributed under the Boost Software License, //
// Version 1.0. (See accompanying file LICENSE_1_0.txt //
// or copy at http://www.boost.org/LICENSE_1_0.txt) //
Expand All @@ -11,8 +11,9 @@
// When using local Boost-develop branch, use specific include paths.
// -I/mnt/c/boost/modular_boost/boost/libs/config/include -I/mnt/c/boost/modular_boost/boost/libs/multiprecision/include

// When using -std=c++14 and g++
// When using -std=c++14/20 and g++
// g++ -finline-functions -march=native -mtune=native -O3 -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -std=c++14 -DWIDE_INTEGER_HAS_LIMB_TYPE_UINT64 -DWIDE_INTEGER_HAS_MUL_8_BY_8_UNROLL -I. -I/mnt/c/boost/boost_1_83_0 -pthread -lpthread test/test.cpp test/test_uintwide_t_boost_backend.cpp test/test_uintwide_t_edge_cases.cpp test/test_uintwide_t_examples.cpp test/test_uintwide_t_float_convert.cpp test/test_uintwide_t_int_convert.cpp test/test_uintwide_t_n_base.cpp test/test_uintwide_t_n_binary_ops_base.cpp examples/example000a_builtin_convert.cpp test/test_uintwide_t_spot_values.cpp examples/example000_numeric_limits.cpp examples/example001_mul_div.cpp examples/example001a_div_mod.cpp examples/example002_shl_shr.cpp examples/example003_sqrt.cpp examples/example003a_cbrt.cpp examples/example004_rootk_pow.cpp examples/example005_powm.cpp examples/example005a_pow_factors_of_p99.cpp examples/example006_gcd.cpp examples/example007_random_generator.cpp examples/example008_miller_rabin_prime.cpp examples/example008a_miller_rabin_prime.cpp examples/example009_timed_mul.cpp examples/example009a_timed_mul_4_by_4.cpp examples/example009b_timed_mul_8_by_8.cpp examples/example010_uint48_t.cpp examples/example011_uint24_t.cpp examples/example012_rsa_crypto.cpp examples/example013_ecdsa_sign_verify.cpp examples/example014_pi_spigot_wide.cpp -o wide_integer.exe
// g++ -finline-functions -march=native -mtune=native -O3 -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -std=c++20 -DWIDE_INTEGER_HAS_LIMB_TYPE_UINT64 -DWIDE_INTEGER_HAS_MUL_8_BY_8_UNROLL -I. -I/mnt/c/boost/boost_1_83_0 -pthread -lpthread test/test.cpp test/test_uintwide_t_boost_backend.cpp test/test_uintwide_t_edge_cases.cpp test/test_uintwide_t_examples.cpp test/test_uintwide_t_float_convert.cpp test/test_uintwide_t_int_convert.cpp test/test_uintwide_t_n_base.cpp test/test_uintwide_t_n_binary_ops_base.cpp examples/example000a_builtin_convert.cpp test/test_uintwide_t_spot_values.cpp examples/example000_numeric_limits.cpp examples/example001_mul_div.cpp examples/example001a_div_mod.cpp examples/example002_shl_shr.cpp examples/example003_sqrt.cpp examples/example003a_cbrt.cpp examples/example004_rootk_pow.cpp examples/example005_powm.cpp examples/example005a_pow_factors_of_p99.cpp examples/example006_gcd.cpp examples/example007_random_generator.cpp examples/example008_miller_rabin_prime.cpp examples/example008a_miller_rabin_prime.cpp examples/example009_timed_mul.cpp examples/example009a_timed_mul_4_by_4.cpp examples/example009b_timed_mul_8_by_8.cpp examples/example010_uint48_t.cpp examples/example011_uint24_t.cpp examples/example012_rsa_crypto.cpp examples/example013_ecdsa_sign_verify.cpp examples/example014_pi_spigot_wide.cpp -o wide_integer.exe

// cd .tidy/make
// make prepare -f make_tidy_01_generic.gmk MY_BOOST_ROOT=/mnt/c/boost/boost_1_83_0
Expand Down
Loading

0 comments on commit 296f2c0

Please sign in to comment.