Skip to content
Merged
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
10 changes: 6 additions & 4 deletions doc/modules/ROOT/pages/examples.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,23 @@ 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

=== int128_t Construction ===
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
----
====

Expand Down
27 changes: 19 additions & 8 deletions doc/modules/ROOT/pages/literals.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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.
34 changes: 23 additions & 11 deletions examples/construction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ int main()
std::cout << " Equals numeric_limits max? " << std::boolalpha
<< (max_value == std::numeric_limits<uint128_t>::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;

Expand All @@ -57,12 +60,21 @@ int main()
std::cout << " Equals numeric_limits min? "
<< (min_value == std::numeric_limits<int128_t>::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)};
Expand All @@ -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;
Expand Down
11 changes: 7 additions & 4 deletions examples/to_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<int128_t>::max()};
std::cout << "int128_t max: " << to_string(large_positive) << std::endl;

return 0;
Expand Down
20 changes: 0 additions & 20 deletions include/boost/int128/literals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {};
Expand All @@ -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 {};
Expand Down
Loading