Skip to content

Commit a793f10

Browse files
authored
Merge pull request #395 from cppalliance/391
Update numeric literals and documentation
2 parents cf2f0d4 + ccdf818 commit a793f10

5 files changed

Lines changed: 55 additions & 47 deletions

File tree

doc/modules/ROOT/pages/examples.adoc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,23 @@ From builtin (42U): 42
2828
From parts (1, 0) = 2^64: 18446744073709551616
2929
From parts (max, max): 340282366920938463463374607431768211455
3030
Equals numeric_limits max? true
31-
From literal "36893488147419103232"_U128: 36893488147419103232
31+
From literal 12345_U128: 12345
3232
From BOOST_INT128_UINT128_C(max): 340282366920938463463374607431768211455
3333
From stringstream: 12345678901234567890123456789
3434
3535
=== int128_t Construction ===
3636
From builtin (-42): -42
3737
From parts (INT64_MIN, 0): -170141183460469231731687303715884105728
3838
Equals numeric_limits min? true
39-
From literal "-99999999999999999999"_i128: -99999999999999999999
40-
From literal "99999999999999999999"_I128: 99999999999999999999
39+
From literal -12345_i128: -12345
40+
From literal 12345_I128: 12345
41+
From BOOST_INT128_INT128_C(-99999999999999999999): -99999999999999999999
42+
From string literal: -99999999999999999999
4143
From BOOST_INT128_INT128_C(min): -170141183460469231731687303715884105728
4244
4345
=== Default and Copy Construction ===
4446
Default constructed: 0
45-
Copy constructed: 36893488147419103232
47+
Copy constructed: 340282366920938463463374607431768211455
4648
----
4749
====
4850

doc/modules/ROOT/pages/literals.adoc

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(const char* str, s
2626
2727
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(const char* str, std::size_t len) noexcept;
2828
29-
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(unsigned long long v) noexcept;
30-
31-
BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(unsigned long long v) noexcept;
32-
3329
BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str) noexcept;
3430
3531
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
3834
3935
BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str, std::size_t len) noexcept;
4036
41-
BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(unsigned long long v) noexcept;
42-
43-
BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(unsigned long long v) noexcept;
44-
4537
} // namespace literals
4638
} // namespace int128
4739
} // namespace boost
@@ -54,3 +46,22 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(unsigned long long
5446
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.
5547

5648
See the xref:examples.adoc#examples_construction[construction examples] for usage demonstrations of both literals and macros.
49+
50+
== Design Rationale
51+
52+
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`.
53+
This holds even for raw numeric tokens like `12345_U128`, which the compiler forwards to the operator as the string `"12345"`.
54+
The choice is intentional.
55+
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.
56+
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.
57+
The API is the same regardless of how large the value is.
58+
The trade-off is that every literal pays the cost of parsing, even when the value would fit in a builtin integer.
59+
For values smaller than 2^64, prefer the constructor:
60+
61+
[source, c++]
62+
----
63+
constexpr uint128_t small {42U}; // direct conversion from a builtin
64+
const auto small_literal {42_U128}; // parses "42" via from_chars
65+
----
66+
67+
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.

examples/construction.cpp

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ int main()
2929
std::cout << " Equals numeric_limits max? " << std::boolalpha
3030
<< (max_value == std::numeric_limits<uint128_t>::max()) << std::endl;
3131

32-
// 3) From user-defined literals (values > 2^64 without splitting)
32+
// 3) From user-defined literals.
33+
// The library provides only string-form UDLs
34+
// For small values like this a string is still parsed rather than direct construction
35+
// Using the constructors for values that fit in (unsigned) long long should be preferred for performance
3336
using namespace boost::int128::literals;
34-
const auto from_literal {"36893488147419103232"_U128}; // 2 * 2^64
35-
std::cout << "From literal \"36893488147419103232\"_U128: " << from_literal << std::endl;
37+
const auto small_literal {12345_U128};
38+
std::cout << "From literal 12345_U128: " << small_literal << std::endl;
3639

37-
// 4) From macro (like UINT64_C but for 128-bit)
40+
// 4) From macro (like UINT64_C but for 128-bit), good for values that exceed unsigned long long
3841
const auto from_macro {BOOST_INT128_UINT128_C(340282366920938463463374607431768211455)};
3942
std::cout << "From BOOST_INT128_UINT128_C(max): " << from_macro << std::endl;
4043

@@ -57,12 +60,21 @@ int main()
5760
std::cout << " Equals numeric_limits min? "
5861
<< (min_value == std::numeric_limits<int128_t>::min()) << std::endl;
5962

60-
// Signed literals (lowercase and uppercase both work)
61-
const auto negative_literal {"-99999999999999999999"_i128};
62-
std::cout << "From literal \"-99999999999999999999\"_i128: " << negative_literal << std::endl;
63+
// Signed literals. Values that fit in unsigned long long can be written
64+
// directly; the leading minus is parsed as a unary operator on the
65+
// literal result (lowercase and uppercase suffixes both work):
66+
const auto negative_literal {-12345_i128};
67+
std::cout << "From literal -12345_i128: " << negative_literal << std::endl;
6368

64-
const auto positive_literal {"99999999999999999999"_I128};
65-
std::cout << "From literal \"99999999999999999999\"_I128: " << positive_literal << std::endl;
69+
const auto positive_literal {12345_I128};
70+
std::cout << "From literal 12345_I128: " << positive_literal << std::endl;
71+
72+
// For magnitudes beyond unsigned long long you can use the macro or a string literal
73+
const auto large_signed {BOOST_INT128_INT128_C(-99999999999999999999)};
74+
std::cout << "From BOOST_INT128_INT128_C(-99999999999999999999): " << large_signed << std::endl;
75+
76+
const auto large_signed_string {"-99999999999999999999"_i128};
77+
std::cout << "From string literal: " << large_signed_string << std::endl;
6678

6779
// Signed macro
6880
const auto from_signed_macro {BOOST_INT128_INT128_C(-170141183460469231731687303715884105728)};
@@ -71,11 +83,11 @@ int main()
7183
std::cout << "\n=== Default and Copy Construction ===" << std::endl;
7284

7385
// Default construction (zero-initialized)
74-
uint128_t default_constructed {};
86+
constexpr uint128_t default_constructed {};
7587
std::cout << "Default constructed: " << default_constructed << std::endl;
7688

7789
// Copy construction
78-
uint128_t copied {from_literal};
90+
const uint128_t copied {from_macro};
7991
std::cout << "Copy constructed: " << copied << std::endl;
8092

8193
return 0;

examples/to_string.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ int main()
3434
std::cout << "Match: " << (u_max64_str == u_max64_std) << std::endl;
3535

3636
// Values beyond 64-bit range
37-
const auto large_unsigned {"340282366920938463463374607431768211455"_U128};
37+
const auto large_unsigned {340282366920938463463374607431768211455_U128};
3838
std::cout << "\nuint128_t max: " << to_string(large_unsigned) << std::endl;
3939

4040
std::cout << "\n=== to_string with int128_t ===" << std::endl;
@@ -55,10 +55,13 @@ int main()
5555
std::cout << "Match: " << (s_large_str == s_large_std) << std::endl;
5656

5757
// Values beyond 64-bit range
58-
const auto large_negative {"-170141183460469231731687303715884105728"_i128};
59-
std::cout << "\nint128_t min: " << to_string(large_negative) << std::endl;
58+
const auto large_negative {-170141183460469231731687303715884105728_i128};
59+
std::cout << "\nint128_t min with string literal: " << to_string(large_negative) << std::endl;
6060

61-
const auto large_positive {"170141183460469231731687303715884105727"_I128};
61+
const auto large_negative_c {BOOST_INT128_INT128_C(-170141183460469231731687303715884105728)};
62+
std::cout << "\nint128_t min with INT128_C macro: " << to_string(large_negative_c) << std::endl;
63+
64+
const auto large_positive {std::numeric_limits<int128_t>::max()};
6265
std::cout << "int128_t max: " << to_string(large_positive) << std::endl;
6366

6467
return 0;

include/boost/int128/literals.hpp

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,6 @@ BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U12
4343
return result;
4444
}
4545

46-
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(unsigned long long v) noexcept
47-
{
48-
return uint128_t{v};
49-
}
50-
51-
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(unsigned long long v) noexcept
52-
{
53-
return uint128_t{v};
54-
}
55-
5646
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str) noexcept
5747
{
5848
int128_t result {};
@@ -67,16 +57,6 @@ BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str) no
6757
return result;
6858
}
6959

70-
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(unsigned long long v) noexcept
71-
{
72-
return int128_t{v};
73-
}
74-
75-
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(unsigned long long v) noexcept
76-
{
77-
return int128_t{v};
78-
}
79-
8060
BOOST_INT128_EXPORT BOOST_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str, std::size_t len) noexcept
8161
{
8262
int128_t result {};

0 commit comments

Comments
 (0)