From 33baa6927abff18dcbe2a9ed5ae093a73a8009a4 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 7 May 2026 09:30:21 -0400 Subject: [PATCH 1/5] Update construction examples --- doc/modules/ROOT/pages/examples.adoc | 10 +++++---- examples/construction.cpp | 32 ++++++++++++++++++---------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/doc/modules/ROOT/pages/examples.adoc b/doc/modules/ROOT/pages/examples.adoc index 7a8a4693..6a8137ce 100644 --- a/doc/modules/ROOT/pages/examples.adoc +++ b/doc/modules/ROOT/pages/examples.adoc @@ -28,7 +28,7 @@ From builtin (42U): 42 From parts (1, 0) = 2^64: 18446744073709551616 From parts (max, max): 340282366920938463463374607431768211455 Equals numeric_limits max? true -From literal "36893488147419103232"_U128: 36893488147419103232 +From literal 12345_U128: 12345 From BOOST_INT128_UINT128_C(max): 340282366920938463463374607431768211455 From stringstream: 12345678901234567890123456789 @@ -36,13 +36,15 @@ From stringstream: 12345678901234567890123456789 From builtin (-42): -42 From parts (INT64_MIN, 0): -170141183460469231731687303715884105728 Equals numeric_limits min? true -From literal "-99999999999999999999"_i128: -99999999999999999999 -From literal "99999999999999999999"_I128: 99999999999999999999 +From literal -12345_i128: -12345 +From literal 12345_I128: 12345 +From BOOST_INT128_INT128_C(-99999999999999999999): -99999999999999999999 +From string literal: -99999999999999999999 From BOOST_INT128_INT128_C(min): -170141183460469231731687303715884105728 === Default and Copy Construction === Default constructed: 0 -Copy constructed: 36893488147419103232 +Copy constructed: 340282366920938463463374607431768211455 ---- ==== diff --git a/examples/construction.cpp b/examples/construction.cpp index d7b234b7..8e88a06b 100644 --- a/examples/construction.cpp +++ b/examples/construction.cpp @@ -29,12 +29,13 @@ int main() std::cout << " Equals numeric_limits max? " << std::boolalpha << (max_value == std::numeric_limits::max()) << std::endl; - // 3) From user-defined literals (values > 2^64 without splitting) + // 3) From user-defined literals. Values that fit in unsigned long long + // can be written directly without quotes: using namespace boost::int128::literals; - const auto from_literal {"36893488147419103232"_U128}; // 2 * 2^64 - std::cout << "From literal \"36893488147419103232\"_U128: " << from_literal << std::endl; + const auto small_literal {12345_U128}; + std::cout << "From literal 12345_U128: " << small_literal << std::endl; - // 4) From macro (like UINT64_C but for 128-bit) + // 4) From macro (like UINT64_C but for 128-bit), good for values that exceed unsigned long long const auto from_macro {BOOST_INT128_UINT128_C(340282366920938463463374607431768211455)}; std::cout << "From BOOST_INT128_UINT128_C(max): " << from_macro << std::endl; @@ -57,12 +58,21 @@ int main() std::cout << " Equals numeric_limits min? " << (min_value == std::numeric_limits::min()) << std::endl; - // Signed literals (lowercase and uppercase both work) - const auto negative_literal {"-99999999999999999999"_i128}; - std::cout << "From literal \"-99999999999999999999\"_i128: " << negative_literal << std::endl; + // Signed literals. Values that fit in unsigned long long can be written + // directly; the leading minus is parsed as a unary operator on the + // literal result (lowercase and uppercase suffixes both work): + const auto negative_literal {-12345_i128}; + std::cout << "From literal -12345_i128: " << negative_literal << std::endl; - const auto positive_literal {"99999999999999999999"_I128}; - std::cout << "From literal \"99999999999999999999\"_I128: " << positive_literal << std::endl; + const auto positive_literal {12345_I128}; + std::cout << "From literal 12345_I128: " << positive_literal << std::endl; + + // For magnitudes beyond unsigned long long you can use the macro or a string literal + const auto large_signed {BOOST_INT128_INT128_C(-99999999999999999999)}; + std::cout << "From BOOST_INT128_INT128_C(-99999999999999999999): " << large_signed << std::endl; + + const auto large_signed_string {"-99999999999999999999"_i128}; + std::cout << "From string literal: " << large_signed_string << std::endl; // Signed macro const auto from_signed_macro {BOOST_INT128_INT128_C(-170141183460469231731687303715884105728)}; @@ -71,11 +81,11 @@ int main() std::cout << "\n=== Default and Copy Construction ===" << std::endl; // Default construction (zero-initialized) - uint128_t default_constructed {}; + constexpr uint128_t default_constructed {}; std::cout << "Default constructed: " << default_constructed << std::endl; // Copy construction - uint128_t copied {from_literal}; + const uint128_t copied {from_macro}; std::cout << "Copy constructed: " << copied << std::endl; return 0; From 838f8bb3e7010d6c143b5253f97c05838c8489d6 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 11 May 2026 14:40:36 -0400 Subject: [PATCH 2/5] Remove unsigned long long literal overloads --- include/boost/int128/literals.hpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/include/boost/int128/literals.hpp b/include/boost/int128/literals.hpp index 9497f20b..ecb85bf8 100644 --- a/include/boost/int128/literals.hpp +++ b/include/boost/int128/literals.hpp @@ -43,16 +43,6 @@ BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U12 return result; } -BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(unsigned long long v) noexcept -{ - return uint128_t{v}; -} - -BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(unsigned long long v) noexcept -{ - return uint128_t{v}; -} - BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str) noexcept { int128_t result {}; @@ -67,16 +57,6 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str) no return result; } -BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(unsigned long long v) noexcept -{ - return int128_t{v}; -} - -BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(unsigned long long v) noexcept -{ - return int128_t{v}; -} - BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str, std::size_t len) noexcept { int128_t result {}; From f9c6a7c1754774bee4b1965717b597ebde8176a2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 11 May 2026 14:42:48 -0400 Subject: [PATCH 3/5] Update example to use literals --- examples/to_string.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/to_string.cpp b/examples/to_string.cpp index a359d14e..a259f149 100644 --- a/examples/to_string.cpp +++ b/examples/to_string.cpp @@ -34,7 +34,7 @@ int main() std::cout << "Match: " << (u_max64_str == u_max64_std) << std::endl; // Values beyond 64-bit range - const auto large_unsigned {"340282366920938463463374607431768211455"_U128}; + const auto large_unsigned {340282366920938463463374607431768211455_U128}; std::cout << "\nuint128_t max: " << to_string(large_unsigned) << std::endl; std::cout << "\n=== to_string with int128_t ===" << std::endl; @@ -55,10 +55,13 @@ int main() std::cout << "Match: " << (s_large_str == s_large_std) << std::endl; // Values beyond 64-bit range - const auto large_negative {"-170141183460469231731687303715884105728"_i128}; - std::cout << "\nint128_t min: " << to_string(large_negative) << std::endl; + const auto large_negative {-170141183460469231731687303715884105728_i128}; + std::cout << "\nint128_t min with string literal: " << to_string(large_negative) << std::endl; - const auto large_positive {"170141183460469231731687303715884105727"_I128}; + const auto large_negative_c {BOOST_INT128_INT128_C(-170141183460469231731687303715884105728)}; + std::cout << "\nint128_t min with INT128_C macro: " << to_string(large_negative_c) << std::endl; + + const auto large_positive {std::numeric_limits::max()}; std::cout << "int128_t max: " << to_string(large_positive) << std::endl; return 0; From 3080a9a1d338b8e06cd5d57fc8e7bdffc12c1c7e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 11 May 2026 15:31:30 -0400 Subject: [PATCH 4/5] Add rationale to literals.adoc --- doc/modules/ROOT/pages/literals.adoc | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/doc/modules/ROOT/pages/literals.adoc b/doc/modules/ROOT/pages/literals.adoc index bb4a5a0f..979c5644 100644 --- a/doc/modules/ROOT/pages/literals.adoc +++ b/doc/modules/ROOT/pages/literals.adoc @@ -26,10 +26,6 @@ BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(const char* str, s BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(const char* str, std::size_t len) noexcept; -BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(unsigned long long v) noexcept; - -BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(unsigned long long v) noexcept; - BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str) noexcept; BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str) noexcept; @@ -38,10 +34,6 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str, st BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str, std::size_t len) noexcept; -BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(unsigned long long v) noexcept; - -BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(unsigned long long v) noexcept; - } // namespace literals } // namespace int128 } // namespace boost @@ -54,3 +46,22 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(unsigned long long The macros at the end allow you to write out a 128-bit number like you would with say `UINT64_C` without having to add quotes. See the xref:examples.adoc#examples_construction[construction examples] for usage demonstrations of both literals and macros. + +== Design Rationale + +All of the user-defined literals provided by the library are string-form: the operator receives a `const char*` and parses the digit sequence via `from_chars`. +This holds even for raw numeric tokens like `12345_U128`, which the compiler forwards to the operator as the string `"12345"`. +The choice is intentional. +A 128-bit value cannot be represented by `unsigned long long`, so any literal whose magnitude exceeds 2^64 must go through a string-based parse. +Providing only the string form means there is a single overload that handles every magnitude uniformly, rather than a numeric form for small values and a string form for large ones with a hard cutoff at 2^64. +The API is the same regardless of how large the value is. +The trade-off is that every literal pays the cost of parsing, even when the value would fit in a builtin integer. +For values smaller than 2^64, prefer the constructor: + +[source, c++] +---- +constexpr uint128_t small {42U}; // direct conversion from a builtin +const auto small_literal {42_U128}; // parses "42" via from_chars +---- + +The two produce the same value, but the constructor avoids the parse and should be used in hot paths or in code where many small constants are constructed. From ccdf8180e9ef4ffc208852fda45fbb5aa9a5f7dc Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 11 May 2026 15:35:18 -0400 Subject: [PATCH 5/5] Update comment on construction --- examples/construction.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/construction.cpp b/examples/construction.cpp index 8e88a06b..644e2b6a 100644 --- a/examples/construction.cpp +++ b/examples/construction.cpp @@ -29,8 +29,10 @@ int main() std::cout << " Equals numeric_limits max? " << std::boolalpha << (max_value == std::numeric_limits::max()) << std::endl; - // 3) From user-defined literals. Values that fit in unsigned long long - // can be written directly without quotes: + // 3) From user-defined literals. + // The library provides only string-form UDLs + // For small values like this a string is still parsed rather than direct construction + // Using the constructors for values that fit in (unsigned) long long should be preferred for performance using namespace boost::int128::literals; const auto small_literal {12345_U128}; std::cout << "From literal 12345_U128: " << small_literal << std::endl;