Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 43 additions & 6 deletions include/boost/int128/detail/mini_from_chars.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace impl {
BOOST_INT128_INLINE_CONSTEXPR unsigned char uchar_values[] =
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
Expand All @@ -47,15 +47,15 @@ static_assert(sizeof(uchar_values) == 256, "uchar_values should represent all 25

#endif // __NVCC__

// Convert characters for 0-9, A-Z, a-z to 0-35. Anything else is 255
// Convert characters for 0-9, A-Z, a-z to 0-35. The digit separator ' is 254. Anything else is 255
BOOST_INT128_HOST_DEVICE BOOST_INT128_FORCE_INLINE constexpr auto digit_from_char(char val) noexcept -> unsigned char
{
#if defined(BOOST_INT128_HAS_GPU_SUPPORT)

constexpr unsigned char uchar_values[] =
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
Expand All @@ -77,10 +77,11 @@ BOOST_INT128_HOST_DEVICE BOOST_INT128_FORCE_INLINE constexpr auto digit_from_cha
return uchar_values[static_cast<unsigned char>(val)];
}

template <typename Integer, typename Unsigned_Integer>
template <typename Integer, typename Unsigned_Integer, bool is_literal_parse = false>
BOOST_INT128_HOST_DEVICE constexpr int from_chars_integer_impl(const char* first, const char* last, Integer& value, int base) noexcept
{
if (first >= last)
if (last - first <= 0)
{
return EINVAL;
}
Expand Down Expand Up @@ -152,7 +153,19 @@ BOOST_INT128_HOST_DEVICE constexpr int from_chars_integer_impl(const char* first

for (; i < fast_limit; ++i)
{
const auto current_digit = static_cast<Unsigned_Integer>(digit_from_char(*next));
const auto raw_digit = digit_from_char(*next);

// When parsing a user-defined literal skip the digit separator ' (marked as 254)
BOOST_INT128_IF_CONSTEXPR (is_literal_parse)
{
if (raw_digit == 254)
{
++next;
continue;
}
}

const auto current_digit = static_cast<Unsigned_Integer>(raw_digit);

if (current_digit >= unsigned_base)
{
Expand All @@ -165,7 +178,19 @@ BOOST_INT128_HOST_DEVICE constexpr int from_chars_integer_impl(const char* first

for (; i < nc; ++i)
{
const auto current_digit = static_cast<Unsigned_Integer>(digit_from_char(*next));
const auto raw_digit = digit_from_char(*next);

// When parsing a user-defined literal skip the digit separator ' (marked as 254)
BOOST_INT128_IF_CONSTEXPR (is_literal_parse)
{
if (raw_digit == 254)
{
++next;
continue;
}
}

const auto current_digit = static_cast<Unsigned_Integer>(raw_digit);

if (current_digit >= unsigned_base)
{
Expand Down Expand Up @@ -229,6 +254,18 @@ BOOST_INT128_TEST_EXPORT BOOST_INT128_HOST_DEVICE constexpr int from_chars(const
return impl::from_chars_integer_impl<int128_t, uint128_t>(first, last, value, base);
}

// Parsing entry points for the user-defined literals. Unlike from_chars these skip the
// C++ digit separator ' so that literals such as 1'234'567_u128 are accepted.
BOOST_INT128_TEST_EXPORT BOOST_INT128_HOST_DEVICE constexpr int from_chars_literal(const char* first, const char* last, uint128_t& value, int base = 10) noexcept
{
return impl::from_chars_integer_impl<uint128_t, uint128_t, true>(first, last, value, base);
}

BOOST_INT128_TEST_EXPORT BOOST_INT128_HOST_DEVICE constexpr int from_chars_literal(const char* first, const char* last, int128_t& value, int base = 10) noexcept
{
return impl::from_chars_integer_impl<int128_t, uint128_t, true>(first, last, value, base);
}

} // namespace detail
} // namespace int128
} // namespace boost
Expand Down
16 changes: 8 additions & 8 deletions include/boost/int128/literals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,56 +18,56 @@ namespace literals {
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(const char* str) noexcept
{
uint128_t result {};
detail::from_chars(str, str + detail::strlen(str), result);
detail::from_chars_literal(str, str + detail::strlen(str), result);
return result;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(const char* str) noexcept
{
uint128_t result {};
detail::from_chars(str, str + detail::strlen(str), result);
detail::from_chars_literal(str, str + detail::strlen(str), result);
return result;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(const char* str, std::size_t len) noexcept
{
uint128_t result {};
detail::from_chars(str, str + len, result);
detail::from_chars_literal(str, str + len, result);
return result;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(const char* str, std::size_t len) noexcept
{
uint128_t result {};
detail::from_chars(str, str + len, result);
detail::from_chars_literal(str, str + len, result);
return result;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str) noexcept
{
int128_t result {};
detail::from_chars(str, str + detail::strlen(str), result);
detail::from_chars_literal(str, str + detail::strlen(str), result);
return result;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str) noexcept
{
int128_t result {};
detail::from_chars(str, str + detail::strlen(str), result);
detail::from_chars_literal(str, str + detail::strlen(str), result);
return result;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str, std::size_t len) noexcept
{
int128_t result {};
detail::from_chars(str, str + len, result);
detail::from_chars_literal(str, str + len, result);
return result;
}

BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str, std::size_t len) noexcept
{
int128_t result {};
detail::from_chars(str, str + len, result);
detail::from_chars_literal(str, str + len, result);
return result;
}

Expand Down
58 changes: 58 additions & 0 deletions test/test_literals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,34 @@ void test_u128_literals()
BOOST_TEST(max_val == macro_val);
}

// Warning with only MSVC 14.1
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4307)
#endif

void test_u128_digit_separators()
{
// C++ digit separators (') are ignored inside numeric literals
BOOST_TEST(boost::int128::uint128_t{1234567} == 1'234'567_u128);
BOOST_TEST(boost::int128::uint128_t{1000} == 1'000_U128);
BOOST_TEST(boost::int128::uint128_t{1234} == 12'34_u128);

// Separators are also honored through the convenience macro
BOOST_TEST(boost::int128::uint128_t{1234} == BOOST_INT128_UINT128_C(1'234));

// The string form of the literal skips separators as well
BOOST_TEST(boost::int128::uint128_t{1234} == "1'234"_u128);
BOOST_TEST(boost::int128::uint128_t{1234} == "1'234"_U128);

// Full-width value with a separator between every group of three digits
const boost::int128::uint128_t max_val {std::numeric_limits<boost::int128::uint128_t>::max()};
BOOST_TEST(max_val == 340'282'366'920'938'463'463'374'607'431'768'211'455_u128);

// Separators must be usable in a constant expression
static_assert(100'000_u128 == boost::int128::uint128_t{100000}, "constexpr separator");
}

void test_i128_literals()
{
BOOST_TEST(boost::int128::int128_t{0} == 0_i128);
Expand All @@ -49,10 +77,40 @@ void test_i128_literals()
BOOST_TEST("-42"_i128 == -42);
}

void test_i128_digit_separators()
{
// C++ digit separators (') are ignored inside numeric literals
BOOST_TEST(boost::int128::int128_t{1000} == 1'000_i128);
BOOST_TEST(boost::int128::int128_t{9999} == 9'999_I128);

// A leading unary minus is applied after the (separated) literal is parsed
BOOST_TEST(boost::int128::int128_t{-1000000} == -1'000'000_i128);

// Separators are also honored through the convenience macro
BOOST_TEST(boost::int128::int128_t{9999} == BOOST_INT128_INT128_C(9'999));

// The string form of the literal skips separators as well
BOOST_TEST(boost::int128::int128_t{1234} == "1'234"_i128);
BOOST_TEST(boost::int128::int128_t{1234} == "1'234"_I128);

// Full-width value with a separator between every group of three digits
const boost::int128::int128_t max_val {std::numeric_limits<boost::int128::int128_t>::max()};
BOOST_TEST(max_val == 170'141'183'460'469'231'731'687'303'715'884'105'727_i128);

// Separators must be usable in a constant expression
static_assert(100'000_i128 == boost::int128::int128_t{100000}, "constexpr separator");
}

#ifdef _MSC_VER
# pragma warning(pop)
#endif

int main()
{
test_u128_literals();
test_u128_digit_separators();
test_i128_literals();
test_i128_digit_separators();

return boost::report_errors();
}
Loading