Literals

The following literals and macros are provided for convenient construction of the types provided in the library.

#include <boost/int128/literals.hpp>

namespace boost {
namespace int128 {
namespace literals {

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(const char* str) noexcept;

BOOST_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(const char* str) noexcept;

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(const char* str, std::size_t len) 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;

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(const char* str, std::size_t len) noexcept;

} // namespace literals
} // namespace int128
} // namespace boost

#define BOOST_INT128_UINT128_C(x) ...
#define BOOST_INT128_INT128_C(x) ...

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 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:

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.