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/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. diff --git a/examples/construction.cpp b/examples/construction.cpp index d7b234b7..644e2b6a 100644 --- a/examples/construction.cpp +++ b/examples/construction.cpp @@ -29,12 +29,15 @@ 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. + // 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 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 +60,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 +83,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; 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; 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 {};