Int128: Portable and Performant 128-bit integers
Matt Borland
Overview
Description
Boost.Int128 is a portable implementation of a signed, and an unsigned 128-bit integer and related functionality (e.g. bit counting).
Importantly, on all platforms, the sizeof
the types provided in this library are 128-bits.
The library is header-only, has no dependencies, and requires only C++14.
Motivation
128-bit integers are remarkably useful in a number of domains, but portability is often an issue.
An example is a 64-bit machine running Linux (say Ubuntu 24.04) has __int128
, but the same exact machine running Windows does not have this type.
Use Cases
Anywhere 128-bits are needed.
Supported Compilers
Boost.Int128 is tested natively on Ubuntu (x86_64, s390x, and aarch64), macOS (x86_64, and Apple Silicon), and Windows (x32 and x64); as well as emulated PPC64LE using QEMU with the following compilers:
-
GCC 5 and later
-
Clang 5 and later
-
Visual Studio 2017 (14.1) and later
-
Intel OneAPI DPC++ 2024.2 and later
Tested on Github Actions and Drone. Coverage can be found on Codecov.
API Reference
Namespaces
-
boost::int128
contains all components of the library
Structures and Classes
Functions
Listed by analogous STL header.
<bit>
<cmath>
-
abs
- Absolute Value
<iostream>
<numeric>
Enums
-
None
Constants
-
None
Concepts
Macros
Configuration
-
BOOST_INT128_ALLOW_SIGN_CONVERSION
- Allows operations between this library’s types, and built-in types of opposite signedness. DISABLED BY DEFAULT FOR CORRECTNESS
Concepts
The following are the definitions of the concepts used throughout the library to ensure consistency.
Signed Integer
This is an extension of the <type_traits>
definition to allow builtin __int128
to be used.
namespace boost {
namespace int128 {
namespace detail {
template <typename T>
struct signed_integer
{
static constexpr bool value = (std::is_signed<T>::value && std::is_integral<T>::value)
#ifdef BOOST_INT128_HAS_INT128
|| std::is_same<T, builtin_i128>::value;
#endif
;
};
template <typename T>
static constexpr bool is_signed_integer_v = signed_integer<T>::value;
#define BOOST_INT128_DEFAULTED_SIGNED_INTEGER_CONCEPT typename SignedInteger, std::enable_if_t<detail::is_signed_integer_v<SignedInteger>, bool> = true
#define BOOST_INT128_SIGNED_INTEGER_CONCEPT typename SignedInteger, std::enable_if_t<detail::is_signed_integer_v<SignedInteger>, bool>
} // namespace detail
} // namespace int128
} // namespace boost
Unsigned Integer
This is an extension of the <type_traits>
definition to allow builtin unsigned __int128
to be used.
namespace boost {
namespace int128 {
namespace detail {
template <typename T>
struct unsigned_integer
{
static constexpr bool value = (std::is_unsigned<T>::value && std::is_integral<T>::value)
#ifdef BOOST_INT128_HAS_INT128
|| std::is_same<T, builtin_u128>::value;
#endif
;
};
template <typename T>
static constexpr bool is_unsigned_integer_v = unsigned_integer<T>::value;
#define BOOST_INT128_DEFAULTED_UNSIGNED_INTEGER_CONCEPT typename UnsignedInteger, std::enable_if_t<detail::is_unsigned_integer_v<UnsignedInteger>, bool> = true
#define BOOST_INT128_UNSIGNED_INTEGER_CONCEPT typename UnsignedInteger, std::enable_if_t<detail::is_unsigned_integer_v<UnsignedInteger>, bool>
} // namespace detail
} // namespace int128
} // namespace boost
Integer
This is a combination of the above two in which any integer can be detected
namespace boost {
namespace int128 {
namespace detail {
template <typename T>
static constexpr bool is_any_integer_v = signed_integer<T>::value || unsigned_integer<T>::value;
#define BOOST_INT128_DEFAULTED_INTEGER_CONCEPT typename Integer, std::enable_if_t<detail::is_any_integer_v<Integer>, bool> = true
#define BOOST_INT128_INTEGER_CONCEPT typename Integer, std::enable_if_t<detail::is_any_integer_v<Integer>, bool>
} // namespace detail
} // namespace int128
} // namespace boost
uint128_t
Description
uint128_t
is an unsigned 128-bit integer
#include <boost/int128.hpp>
namespace boost {
namespace int128 {
struct uint128_t {
#if BOOST_INT128_ENDIAN_LITTLE_BYTE
std::uint64_t low {};
std::uint64_t high {};
#else
std::uint64_t high {};
std::uint64_t low {};
#endif // BOOST_INT128_ENDIAN_LITTLE_BYTE
// Defaulted basic construction
constexpr uint128_t() noexcept = default;
constexpr uint128_t(const uint128_t&) noexcept = default;
constexpr uint128_t(uint128_t&&) noexcept = default;
constexpr uint128_t& operator=(const uint128_t&) noexcept = default;
constexpr uint128_t& operator=(uint128_t&&) noexcept = default;
// Requires conversion file to be implemented
constexpr uint128_t(const int128_t& v) noexcept;
// Construct from integral types
#if BOOST_INT128_ENDIAN_LITTLE_BYTE
constexpr uint128_t(const std::uint64_t hi, const std::uint64_t lo) noexcept;
template <BOOST_INT128_DEFAULTED_SIGNED_INTEGER_CONCEPT>
constexpr uint128_t(const SignedInteger v) noexcept;
template <BOOST_INT128_DEFAULTED_UNSIGNED_INTEGER_CONCEPT>
constexpr uint128_t(const UnsignedInteger v) noexcept;
#ifdef BOOST_INT128_HAS_INT128
constexpr uint128_t(const detail::builtin_i128 v) noexcept;
constexpr uint128_t(const detail::builtin_u128 v) noexcept;
#endif // BOOST_INT128_HAS_INT128
// Integer conversion operators
constexpr operator bool() const noexcept;
template <BOOST_INT128_DEFAULTED_SIGNED_INTEGER_CONCEPT>
explicit constexpr operator SignedInteger() const noexcept;
template <BOOST_INT128_DEFAULTED_UNSIGNED_INTEGER_CONCEPT>
explicit constexpr operator UnsignedInteger() const noexcept;
#ifdef BOOST_INT128_HAS_INT128
explicit constexpr operator detail::builtin_i128() const noexcept;
explicit constexpr operator detail::builtin_u128() const noexcept;
#endif // BOOST_INT128_HAS_INT128
// Conversion to float
// This is basically the same as ldexp(static_cast<T>(high), 64) + static_cast<T>(low),
// but can be constexpr at C++11 instead of C++26
explicit constexpr operator float() const noexcept;
explicit constexpr operator double() const noexcept;
explicit constexpr operator long double() const noexcept;
// Compound OR
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t& operator|=(Integer rhs) noexcept;
constexpr uint128_t& operator|=(uint128_t rhs) noexcept;
// Compound AND
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t& operator&=(Integer rhs) noexcept;
constexpr uint128_t& operator&=(uint128_t rhs) noexcept;
// Compound XOR
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t& operator^=(Integer rhs) noexcept;
constexpr uint128_t& operator^=(uint128_t rhs) noexcept;
// Compound Left Shift
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t& operator<<=(Integer rhs) noexcept;
constexpr uint128_t& operator<<=(uint128_t rhs) noexcept;
// Compound Right Shift
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t& operator>>=(Integer rhs) noexcept;
constexpr uint128_t& operator>>=(uint128_t rhs) noexcept;
constexpr uint128_t& operator++() noexcept;
constexpr uint128_t& operator++(int) noexcept;
constexpr uint128_t& operator--() noexcept;
constexpr uint128_t& operator--(int) noexcept;
// Compound Addition
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t& operator+=(Integer rhs) noexcept;
constexpr uint128_t& operator+=(uint128_t rhs) noexcept;
// Compound Subtraction
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t& operator-=(Integer rhs) noexcept;
constexpr uint128_t& operator-=(uint128_t rhs) noexcept;
// Compound Multiplication
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t& operator*=(Integer rhs) noexcept;
constexpr uint128_t& operator*=(uint128_t rhs) noexcept;
// Compound Division
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t& operator/=(Integer rhs) noexcept;
constexpr uint128_t& operator/=(uint128_t rhs) noexcept;
// Compound modulo
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t& operator%=(Integer rhs) noexcept;
constexpr uint128_t& operator%=(uint128_t rhs) noexcept;
}; // struct uint128_t
} //namespace int128
} //namespace boost
We also have the following non-member free functions:
namespace boost {
namespace int128 {
//=====================================
// Comparison Operators
//=====================================
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator<(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator<(const Integer lhs, const uint128_t rhs) noexcept;
constexpr bool operator<(const uint128_t lhs, const uint128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator<=(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator<=(const Integer lhs, const uint128_t rhs) noexcept;
constexpr bool operator<=(const uint128_t lhs, const uint128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator>(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator>(const Integer lhs, const uint128_t rhs) noexcept;
constexpr bool operator>(const uint128_t lhs, const uint128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator>=(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator>=(const Integer lhs, const uint128_t rhs) noexcept;
constexpr bool operator>=(const uint128_t lhs, const uint128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator==(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator==(const Integer lhs, const uint128_t rhs) noexcept;
constexpr bool operator==(const uint128_t lhs, const uint128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator!=(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator!=(const Integer lhs, const uint128_t rhs) noexcept;
constexpr bool operator!=(const uint128_t lhs, const uint128_t rhs) noexcept;
//=====================================
// Bit-wise Operators
//=====================================
constexpr uint128_t operator~(const uint128_t rhs) noexcept
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t operator|(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t operator|(const Integer lhs, const uint128_t rhs) noexcept;
constexpr uint128_t operator|(const uint128_t lhs, const uint128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t operator&(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t operator&(const Integer lhs, const uint128_t rhs) noexcept;
constexpr uint128_t operator&(const uint128_t lhs, const uint128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t operator^(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t operator^(const Integer lhs, const uint128_t rhs) noexcept;
constexpr uint128_t operator^(const uint128_t lhs, const uint128_t rhs) noexcept;
// Shift operators have a number of overloads to ensure they return type matches the behavior of built-in types
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr uint128_t operator<<(const uint128_t lhs, const Integer rhs) noexcept;
template <typename Integer, std::enable_if_t<std::is_integral<Integer>::value && (sizeof(Integer) * 8 > 16), bool> = true>
constexpr Integer operator<<(const Integer lhs, const uint128_t rhs) noexcept;
template <typename SignedInteger, std::enable_if_t<detail::is_signed_integer_v<SignedInteger> && (sizeof(SignedInteger) * 8 <= 16), bool> = true>
constexpr int operator<<(const SignedInteger lhs, const uint128_t rhs) noexcept;
template <typename UnsignedInteger, std::enable_if_t<detail::is_unsigned_integer_v<UnsignedInteger> && (sizeof(UnsignedInteger) * 8 <= 16), bool> = true>
constexpr unsigned int operator<<(const UnsignedInteger lhs, const uint128_t rhs) noexcept;
constexpr uint128_t operator<<(const uint128_t lhs, const uint128_t rhs) noexcept;
template <typename Integer, std::enable_if_t<std::is_integral<Integer>::value, bool> = true>
constexpr uint128_t operator>>(const uint128_t lhs, const Integer rhs) noexcept;
template <typename Integer, std::enable_if_t<std::is_integral<Integer>::value && (sizeof(Integer) * 8 > 16), bool> = true>
constexpr Integer operator>>(const Integer lhs, const uint128_t rhs) noexcept;
template <typename SignedInteger, std::enable_if_t<detail::is_signed_integer_v<SignedInteger> && (sizeof(SignedInteger) * 8 <= 16), bool> = true>
constexpr int operator>>(const SignedInteger lhs, const uint128_t rhs) noexcept;
template <typename UnsignedInteger, std::enable_if_t<detail::is_unsigned_integer_v<UnsignedInteger> && (sizeof(UnsignedInteger) * 8 <= 16), bool> = true>
constexpr unsigned operator>>(UnsignedInteger lhs, const uint128_t rhs) noexcept;
constexpr uint128_t operator>>(const uint128_t lhs, const uint128_t rhs) noexcept;
//=====================================
// Increment and Decrement Operators
//=====================================
constexpr uint128_t& uint128_t::operator++() noexcept;
constexpr uint128_t& uint128_t::operator++(int) noexcept;
constexpr uint128_t& uint128_t::operator--() noexcept;
constexpr uint128_t& uint128_t::operator--(int) noexcept;
//=====================================
// Add, Sub, Mul, Div, Mod
//=====================================
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr uint128_t operator+(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr uint128_t operator+(const Integer lhs, const uint128_t rhs) noexcept;
constexpr uint128_t operator+(const uint128_t lhs, const uint128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr uint128_t operator-(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr uint128_t operator-(const Integer lhs, const uint128_t rhs) noexcept;
constexpr uint128_t operator-(const uint128_t lhs, const uint128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr uint128_t operator*(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr uint128_t operator*(const Integer lhs, const uint128_t rhs) noexcept;
constexpr uint128_t operator*(const uint128_t lhs, const uint128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr uint128_t operator/(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr uint128_t operator/(const Integer lhs, const uint128_t rhs) noexcept;
constexpr uint128_t operator/(const uint128_t lhs, const uint128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr uint128_t operator%(const uint128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr uint128_t operator%(const Integer lhs, const uint128_t rhs) noexcept;
constexpr uint128_t operator%(const uint128_t lhs, const uint128_t rhs) noexcept;
} // namespace int128
} // namespace boost
int128_t
Description
int128_t
is a signed 128-bit integer
#include <boost/int128.hpp>
namespace boost {
namespace int128 {
struct int128_t {
#if BOOST_INT128_ENDIAN_LITTLE_BYTE
std::uint64_t low {};
std::int64_t high {};
#else
std::int64_t high {};
std::uint64_t low {};
#endif
// Defaulted basic construction
constexpr int128_t() noexcept = default;
constexpr int128_t(const int128_t&) noexcept = default;
constexpr int128_t(int128_t&&) noexcept = default;
constexpr int128_t& operator=(const int128_t&) noexcept = default;
constexpr int128_t& operator=(int128_t&&) noexcept = default;
// Requires conversion file to be implemented
constexpr int128_t(const uint128_t& v) noexcept;
// Construct from integral types
constexpr int128_t(const std::int64_t hi, const std::uint64_t lo) noexcept;
template <BOOST_INT128_DEFAULTED_SIGNED_INTEGER_CONCEPT>
constexpr int128_t(const SignedInteger v) noexcept;
template <BOOST_INT128_DEFAULTED_UNSIGNED_INTEGER_CONCEPT>
constexpr int128_t(const UnsignedInteger v) noexcept;
#ifdef BOOST_INT128_HAS_INT128
constexpr int128_t(const detail::builtin_i128 v) noexcept;
constexpr int128_t(const detail::builtin_u128 v) noexcept;
#endif // BOOST_INT128_HAS_INT128
// Integer Conversion operators
constexpr operator bool() const noexcept;
template <BOOST_INT128_DEFAULTED_SIGNED_INTEGER_CONCEPT>
explicit constexpr operator SignedInteger() const noexcept;
template <BOOST_INT128_DEFAULTED_UNSIGNED_INTEGER_CONCEPT>
explicit constexpr operator UnsignedInteger() const noexcept;
#ifdef BOOST_INT128_HAS_INT128
explicit constexpr operator detail::builtin_i128() const noexcept;
explicit constexpr operator detail::builtin_u128() const noexcept;
#endif // BOOST_INT128_HAS_INT128
// Conversion to float
// This is basically the same as ldexp(static_cast<T>(high), 64) + static_cast<T>(low),
// but can be constexpr at C++11 instead of C++26
explicit constexpr operator float() const noexcept;
explicit constexpr operator double() const noexcept;
explicit constexpr operator long double() const noexcept;
// Compound Or
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t& operator|=(Integer rhs) noexcept;
constexpr int128_t& operator|=(int128_t rhs) noexcept;
// Compound And
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t& operator&=(Integer rhs) noexcept;
constexpr int128_t& operator&=(int128_t rhs) noexcept;
// Compound XOR
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t& operator^=(Integer rhs) noexcept;
constexpr int128_t& operator^=(int128_t rhs) noexcept;
// Compound Left Shift
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t& operator<<=(Integer rhs) noexcept;
constexpr int128_t& operator<<=(int128_t rhs) noexcept;
// Compound Right Shift
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t& operator>>=(Integer rhs) noexcept;
constexpr int128_t& operator>>=(int128_t rhs) noexcept;
// Prefix and postfix increment
constexpr int128_t& operator++() noexcept;
constexpr int128_t& operator++(int) noexcept;
// Prefix and postfix decrment
constexpr int128_t& operator--() noexcept;
constexpr int128_t& operator--(int) noexcept;
// Compound Addition
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t& operator+=(Integer rhs) noexcept;
constexpr int128_t& operator+=(int128_t rhs) noexcept;
// Compound Subtraction
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t& operator-=(Integer rhs) noexcept;
constexpr int128_t& operator-=(int128_t rhs) noexcept;
// Compound Multiplication
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t& operator*=(Integer rhs) noexcept;
constexpr int128_t& operator*=(int128_t rhs) noexcept;
// Compound Division
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t& operator/=(Integer rhs) noexcept;
constexpr int128_t& operator/=(int128_t rhs) noexcept;
// Compound Modulo
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t& operator%=(Integer rhs) noexcept;
constexpr int128_t& operator%=(int128_t rhs) noexcept;
}; // struct int128_t
} //namespace int128
} //namespace boost
We also have the following non-member free functions:
namespace boost {
namespace int128 {
//=====================================
// Comparison Operators
//=====================================
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator<(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator<(const Integer lhs, const int128_t rhs) noexcept;
constexpr bool operator<(const int128_t lhs, const int128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator<=(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator<=(const Integer lhs, const int128_t rhs) noexcept;
constexpr bool operator<=(const int128_t lhs, const int128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator>(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator>(const Integer lhs, const int128_t rhs) noexcept;
constexpr bool operator>(const int128_t lhs, const int128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator>=(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator>=(const Integer lhs, const int128_t rhs) noexcept;
constexpr bool operator>=(const int128_t lhs, const int128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator==(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator==(const Integer lhs, const int128_t rhs) noexcept;
constexpr bool operator==(const int128_t lhs, const int128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator!=(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr bool operator!=(const Integer lhs, const int128_t rhs) noexcept;
constexpr bool operator!=(const int128_t lhs, const int128_t rhs) noexcept;
//=====================================
// Bit-wise Operators
//=====================================
constexpr int128_t operator~(const int128_t rhs) noexcept
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t operator|(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t operator|(const Integer lhs, const int128_t rhs) noexcept;
constexpr int128_t operator|(const int128_t lhs, const int128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t operator&(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t operator&(const Integer lhs, const int128_t rhs) noexcept;
constexpr int128_t operator&(const int128_t lhs, const int128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t operator^(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t operator^(const Integer lhs, const int128_t rhs) noexcept;
constexpr int128_t operator^(const int128_t lhs, const int128_t rhs) noexcept;
// Shift operators have a number of overloads to ensure they return type matches the behavior of built-in types
template <BOOST_INT128_DEFAULTED_INTEGER_CONCEPT>
constexpr int128_t operator<<(const int128_t lhs, const Integer rhs) noexcept;
template <typename Integer, std::enable_if_t<std::is_integral<Integer>::value && (sizeof(Integer) * 8 > 16), bool> = true>
constexpr Integer operator<<(const Integer lhs, const int128_t rhs) noexcept;
template <typename SignedInteger, std::enable_if_t<detail::is_signed_integer_v<SignedInteger> && (sizeof(SignedInteger) * 8 <= 16), bool> = true>
constexpr int operator<<(const SignedInteger lhs, const int128_t rhs) noexcept;
template <typename UnsignedInteger, std::enable_if_t<detail::is_unsigned_integer_v<UnsignedInteger> && (sizeof(UnsignedInteger) * 8 <= 16), bool> = true>
constexpr unsigned int operator<<(const UnsignedInteger lhs, const int128_t rhs) noexcept;
constexpr int128_t operator<<(const int128_t lhs, const int128_t rhs) noexcept;
template <typename Integer, std::enable_if_t<std::is_integral<Integer>::value, bool> = true>
constexpr int128_t operator>>(const int128_t lhs, const Integer rhs) noexcept;
template <typename Integer, std::enable_if_t<std::is_integral<Integer>::value && (sizeof(Integer) * 8 > 16), bool> = true>
constexpr Integer operator>>(const Integer lhs, const int128_t rhs) noexcept;
template <typename SignedInteger, std::enable_if_t<detail::is_signed_integer_v<SignedInteger> && (sizeof(SignedInteger) * 8 <= 16), bool> = true>
constexpr int operator>>(const SignedInteger lhs, const int128_t rhs) noexcept;
template <typename UnsignedInteger, std::enable_if_t<detail::is_unsigned_integer_v<UnsignedInteger> && (sizeof(UnsignedInteger) * 8 <= 16), bool> = true>
constexpr unsigned operator>>(UnsignedInteger lhs, const int128_t rhs) noexcept;
constexpr int128_t operator>>(const int128_t lhs, const int128_t rhs) noexcept;
//=====================================
// Increment and Decrement Operators
//=====================================
constexpr int128_t& int128_t::operator++() noexcept;
constexpr int128_t& int128_t::operator++(int) noexcept;
constexpr int128_t& int128_t::operator--() noexcept;
constexpr int128_t& int128_t::operator--(int) noexcept;
//=====================================
// Add, Sub, Mul, Div, Mod
//=====================================
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr int128_t operator+(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr int128_t operator+(const Integer lhs, const int128_t rhs) noexcept;
constexpr int128_t operator+(const int128_t lhs, const int128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr int128_t operator-(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr int128_t operator-(const Integer lhs, const int128_t rhs) noexcept;
constexpr int128_t operator-(const int128_t lhs, const int128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr int128_t operator*(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr int128_t operator*(const Integer lhs, const int128_t rhs) noexcept;
constexpr int128_t operator*(const int128_t lhs, const int128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr int128_t operator/(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr int128_t operator/(const Integer lhs, const int128_t rhs) noexcept;
constexpr int128_t operator/(const int128_t lhs, const int128_t rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr int128_t operator%(const int128_t lhs, const Integer rhs) noexcept;
template <BOOST_INT128_DEFAULTED_INTEGER_TYPE>
constexpr int128_t operator%(const Integer lhs, const int128_t rhs) noexcept;
constexpr int128_t operator%(const int128_t lhs, const int128_t rhs) noexcept;
} // namespace int128
} // namespace boost
Mixed Type Operations
Conversions
The ability to convert between the two types via static_cast
is available as documented in the above class descriptions.
Comparisons and Arithmetics
The following operations are ALL ILLEGAL.
Since we can not enforce -Wsign-conversion
and -Wsign-compare
through the compiler we instead static_assert
that the operation is unavailable, and that the USER MUST CAST TO A COMMON TYPE.
This removes a common source of error (search "Sign Conversion" on Stack Overflow)
namespace boost {
namespace int128 {
//=====================================
// Comparison Operators
//=====================================
constexpr bool operator==(uint128_t lhs, int128_t rhs);
constexpr bool operator==(int128_t lhs, uint128_t rhs);
constexpr bool operator!=(uint128_t lhs, int128_t rhs);
constexpr bool operator!=(int128_t lhs, uint128_t rhs);
constexpr bool operator<(uint128_t lhs, int128_t rhs);
constexpr bool operator<(int128_t lhs, uint128_t rhs);
constexpr bool operator<=(uint128_t lhs, int128_t rhs);
constexpr bool operator<=(int128_t lhs, uint128_t rhs);
constexpr bool operator>(uint128_t lhs, int128_t rhs);
constexpr bool operator>(int128_t lhs, uint128_t rhs);
constexpr bool operator>=(uint128_t lhs, int128_t rhs);
constexpr bool operator>=(int128_t lhs, uint128_t rhs);
//=====================================
// Arithmetic Operators
//=====================================
constexpr uint128_t operator+(uint128_t lhs, int128_t rhs);
constexpr uint128_t operator+(int128_t lhs, uint128_t rhs);
constexpr uint128_t operator-(uint128_t lhs, int128_t rhs);
constexpr uint128_t operator-(int128_t lhs, uint128_t rhs);
constexpr uint128_t operator*(uint128_t lhs, int128_t rhs);
constexpr uint128_t operator*(int128_t lhs, uint128_t rhs);
constexpr uint128_t operator/(uint128_t lhs, int128_t rhs);
constexpr uint128_t operator/(int128_t lhs, uint128_t rhs);
constexpr uint128_t operator%(uint128_t lhs, int128_t rhs);
constexpr uint128_t operator%(int128_t lhs, uint128_t rhs);
} // namespace int128
} // namespace boost
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 {
constexpr uint128_t operator ""_u128(const char* str) noexcept;
constexpr uint128_t operator ""_U128(const char* str) noexcept;
constexpr uint128_t operator ""_u128(const char* str, std::size_t len) noexcept;
constexpr uint128_t operator ""_U128(const char* str, std::size_t len) noexcept;
constexpr uint128_t operator ""_u128(unsigned long long v) noexcept;
constexpr uint128_t operator ""_U128(unsigned long long v) noexcept;
constexpr int128_t operator ""_i128(const char* str) noexcept;
constexpr int128_t operator ""_I128(const char* str) noexcept;
constexpr int128_t operator ""_i128(unsigned long long v) noexcept;
constexpr int128_t operator ""_I128(unsigned long long v) noexcept;
constexpr int128_t operator ""_i128(const char* str, std::size_t len) noexcept;
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:
#include <boost/int128.hpp>
int main()
{
const boost::int128::uint128_t max_val {std::numeric_limits<boost::int128::uint128_t>::max()};
// The following type for auto will be boost::int128::uint128_t
const auto macro_val{BOOST_INT128_UINT128_C(340282366920938463463374607431768211455)};
return !(max_val == macro_val);
}
<bit>
The following are functions analagous to those found in C++20’s <bit> header, but for boost::int128::uint128_t
.
None of these functions applied to signed integral types, and thus none have overloads for boost::int128::int128_t
.
All of these functions are available using C ++14 like the rest of the library.
has_single_bit
Checks if x
is an integral power of two.
Returns true
if x
is a power of two; otherwise false
namespace boost {
namespace int128 {
constexpr bool has_single_bit(uint128_t x) noexcept;
} // namespace int128
} // namespace boost
countl_zero
Returns the number of consecutive 0
bits in the value x
, starting from the most significant.
namespace boost {
namespace int128 {
constexpr int countl_zero(uint128_t x) noexcept;
} // namespace int128
} // namespace boost
countl_one
Returns the number of consecutive 1
bits in the value x
, starting from the most significant.
namespace boost {
namespace int128 {
constexpr int countl_one(uint128_t x) noexcept;
} // namespace int128
} // namespace boost
bit_width
If x
is not zero, returns the number of bits needed to store the value x
.
If x
is zero, returns 0
namespace boost {
namespace int128 {
constexpr int bit_width(uint128_t x) noexcept;
} // namespace int128
} // namespace boost
bit_ceil
Returns the smallest integral power of two that is not smaller than x
.
namespace boost {
namespace int128 {
constexpr uint128_t bit_ceil(uint128_t x) noexcept;
} // namespace int128
} // namespace boost
bit_floor
Returns the smallest integral power of two that is not greater than x
.
If x
is 0 then returns 0.
namespace boost {
namespace int128 {
constexpr uint128_t bit_floor(uint128_t x) noexcept;
} // namespace int128
} // namespace boost
countr_zero
Returns the number of consecutive 0
bits in the value x
, starting from the least significant.
namespace boost {
namespace int128 {
constexpr int countr_zero(uint128_t x) noexcept;
} // namespace int128
} // namespace boost
countr_one
Returns the number of consecutive 1
bits in the value x
, starting from the least significant.
namespace boost {
namespace int128 {
constexpr int countr_one(uint128_t x) noexcept;
} // namespace int128
} // namespace boost
rotl
Computes the result of bitwise left-rotating the value of x
by s
positions.
This operation is also known as a left circular shift.
namespace boost {
namespace int128 {
constexpr uint128_t rotl(uint128_t x, int s) noexcept;
} // namespace int128
} // namespace boost
rotr
Computes the result of bitwise right-rotating the value of x
by s
positions.
This operation is also known as a right circular shift.
namespace boost {
namespace int128 {
constexpr uint128_t rotr(uint128_t x, int s) noexcept;
} // namespace int128
} // namespace boost
popcount
Returns the number of 1
bits in x
.
namespace boost {
namespace int128 {
constexpr int popcount(uint128_t x) noexcept;
} // namespace int128
} // namespace boost
byteswap
Reverses the bytes in the given integer value x
.
namespace boost {
namespace int128 {
constexpr int byteswap(uint128_t x) noexcept;
} // namespace int128
} // namespace boost
<iostream>
The following stream operators are available, and work similar to built-in integer types:
#include <boost/int128/iostream.hpp>
namespace boost {
namespace int128 {
template <typename charT, typename traits>
std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, const int128_t& v)
template <typename charT, typename traits>
std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& is, int128_t& v)
template <typename charT, typename traits>
std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, const uint128_t& v)
template <typename charT, typename traits>
std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& is, uint128_t& v)
} // namespace int128
} // namespace boost
Flags
The following flags are supported for both streaming directions:
-
std::ios_base::oct
- Octal Numbers -
std::ios_base::dec
- Decimal Numbers -
std::ios_base::hex
- Hexadecimal Numbers -
std::ios_base::upper
- Upper Case Formatting (e.g. 0XFFFF) -
std::ios_base::lower
- Lower Case Formatting (e.g. 0xffff)
<numeric>
Saturating Arithmetic
Saturating arithmetic avoids the possibility of overflow or underflow, by clamping the value to a defined range should either of these situations occur.
This means that on overflow the types will return std::numeric_limits::max()
, and on underflow they will return std::numeric_limits::min()
.
The following functions are provided for saturating arithmetic, and they do not require C++26.
#include <boost/int128/numeric.hpp>
namespace boost {
namespace int128 {
constexpr uint128_t add_sat(uint128_t lhs, uint128_t rhs) noexcept;
constexpr int128_t add_sat(int128_t lhs, int128_t rhs) noexcept;
constexpr uint128_t sub_sat(uint128_t lhs, uint128_t rhs) noexcept;
constexpr int128_t sub_sat(int128_t lhs, int128_t rhs) noexcept;
constexpr uint128_t mul_sat(uint128_t lhs, uint128_t rhs) noexcept;
constexpr int128_t mul_sat(int128_t lhs, int128_t rhs) noexcept;
constexpr uint128_t div_sat(uint128_t lhs, uint128_t rhs) noexcept;
constexpr int128_t div_sat(int128_t lhs, int128_t rhs) noexcept;
} // namespace int128
} // namespace boost
Saturating Cast
This function allows a LibraryIntegerType
(i.e. uint128_t
or int128_t
) to be safely casted to another integer type to include built-in and hardware integer types (TargetIntegerType
).
Should the TargetIntegerType
not be able to represent the value of the LibraryIntegerType
it will be set to either std::numeric_limits::max()
or std::numeric_limits::min()
depending on if the situation is overflow or underflow.
#include <boost/int128/numeric.hpp>
namespace boost {
namespace int128 {
constexpr <typename LibraryIntegerType, typename TargetIntegerType>
constexpr TargetIntegerType saturate_cast(LibraryIntegerType x) noexcept;
} // namespace int128
} // namespace boost
Examples
All the following examples can be found in the examples/ folder of the library.
Basic Construction
#include <boost/int128.hpp>
#include <limits>
#include <cassert>
#include <type_traits>
#include <sstream>
int main()
{
// There are a number of ways to construct integer types
// 1) From a builtin-integer type
constexpr boost::int128::uint128_t from_builtin {6U};
assert(from_builtin == 6U);
// 2) By directly adding 128-bit hex/integer values
constexpr boost::int128::uint128_t from_hex {UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF)};
assert(from_hex == std::numeric_limits<boost::int128::uint128_t>::max());
// 3) From literal which allows values >2^64 to be easily constructed
// To match expectations the literals are in a separate literals namespace
using namespace boost::int128::literals;
const auto from_literal = "36893488147419103232"_U128; // Can be constexpr, but older compilers (GCC <= 7) fail
static_assert(std::is_same<decltype(from_literal), const boost::int128::uint128_t>::value, "uint128_t works fine with auto construction");
// 4) From macro appending the correct literal like UINT32_C, UINT64_C etc.
// This also allows values >2^64 to be constructed without needing to separate into high and low hex values
const auto from_macro {BOOST_INT128_UINT128_C(36893488147419103232)}; // Can be constexpr, but older compilers (GCC <= 7 and Clang <= 8) fail
assert(from_macro == from_literal);
// 5) Istream is supported by the types
std::stringstream in;
in.str("340282366920938463463374607431768211455");
boost::int128::uint128_t from_stream;
in >> from_stream;
assert(from_stream == from_hex);
// These same methods also apply to int128_t
constexpr boost::int128::int128_t from_int {-42};
assert(from_int == -42);
constexpr boost::int128::int128_t from_hex_signed {INT64_MIN, 0};
assert(from_hex_signed == std::numeric_limits<boost::int128::int128_t>::min());
// Both capital and lowercase letters work
const auto negative_literal {"-42"_i128};
assert(negative_literal == from_int);
const auto negative_macro {BOOST_INT128_INT128_C(-170141183460469231731687303715884105728)};
assert(negative_macro == from_hex_signed);
return 0;
}
Basic Arithmetic
#include <boost/int128/int128.hpp>
#include <cassert>
int main()
{
// The types of this library support all arithmetic operations one would expect
// They can be between values of the same type, or same signedness by default.
// See `mixed_type_arithmetic.cpp` for other operations
boost::int128::int128_t x {42};
boost::int128::int128_t y {-2};
// Comparisons
assert(x > y);
assert(x >= y);
assert(y < x);
assert(y <= x);
assert(x == x);
assert(y != x);
// Add,sub,mul,div
assert(x + y == 40);
assert(y - x == -44);
assert(x * y == -84);
assert(x / y == -21);
assert(x % y == 0);
// Absolute Value
// For generic programming add using boost::int128::abs; at the top of the function,
// so that it will be found by ADL just by writing abs(y);
assert(boost::int128::abs(y) == 2);
// Compound operations
x /= y;
assert(x == -21);
y += 4L;
assert(y == 2);
y *= -5LL;
assert(y == -10);
return 0;
}
IO Streaming
#include <boost/int128/int128.hpp>
#include <boost/int128/iostream.hpp>
#include <iostream>
#include <iomanip>
int main()
{
// Both types allow streaming as one would expect from a regular builtin-type
constexpr boost::int128::int128_t signed_value {-42};
std::cout << "Signed value: " << signed_value << std::endl;
// We can also use <iomanip> to change the output format
constexpr boost::int128::uint128_t unsigned_value {0x1, UINT64_MAX};
std::cout << "Unsigned value (dec): " << unsigned_value << '\n'
<< "Unsigned value (hex): " << std::hex << unsigned_value << '\n'
<< "Unsigned value (oct): " << std::oct << unsigned_value << std::endl;
// Hex also can be manipulated to be uppercase
std::cout << "Upper unsigned value: " << std::hex << std::uppercase << unsigned_value << std::endl;
return 0;
}
Bitwise Functions (<bit>)
#include <boost/int128/int128.hpp>
#include <boost/int128/bit.hpp>
int main()
{
// The functions from bit are only available for uint128_t
constexpr boost::int128::uint128_t x {1U};
// All the functions are constexpr
// Does the value have only a single bit set?
static_assert(boost::int128::has_single_bit(x), "Should have one bit");
// How many zeros from the left
static_assert(boost::int128::countl_zero(x) == 127U, "Should be 127");
// The bit width of the value
// 1 + 1 is 10 in binary which is 2 bits wide
static_assert(boost::int128::bit_width(x + x) == 2U, "2 bits wide");
// The smallest power of two not greater than the input value
static_assert(boost::int128::bit_floor(3U * x) == 2U, "2 < 3");
// The smallest power of two not Smaller than the input value
static_assert(boost::int128::bit_ceil(5U * x) == 8U, "8 > 5");
// How many zeros from the right?
static_assert(boost::int128::countr_zero(2U * x) == 1, "1 zero to the right of 10");
// How many 1-bits in the value
static_assert(boost::int128::popcount(7U * x) == 3, "111");
// Swap the bytes
// Create a value with distinct byte pattern
constexpr boost::int128::uint128_t original{
0x0123456789ABCDEFULL,
0xFEDCBA9876543210ULL
};
// Expected result after byteswap
constexpr boost::int128::uint128_t expected{
0x1032547698BADCFEULL,
0xEFCDAB8967452301ULL
};
static_assert(boost::int128::byteswap(original) == expected, "Mismatched byteswap");
static_assert(boost::int128::byteswap(expected) == original, "Mismatched byteswap");
return 0;
}
Saturating Arithmetic (<numeric>)
#include <boost/int128/int128.hpp>
#include <boost/int128/numeric.hpp>
// Or you can do a single header
// #include <boost/int128.hpp>
#include <limits>
#include <type_traits>
#include <cassert>
int main()
{
// std::numeric_limits is overloaded for both types
constexpr auto uint_max {std::numeric_limits<boost::int128::uint128_t>::max()};
static_assert(std::is_same<decltype(uint_max), const boost::int128::uint128_t>::value, "Types should match");
constexpr boost::int128::int128_t int_max {std::numeric_limits<boost::int128::int128_t>::max()};
// Saturating arithmetic returns max on overflow, or min on underflow rather than rolling over
assert(boost::int128::add_sat(uint_max, uint_max) == uint_max);
assert(boost::int128::sub_sat(boost::int128::uint128_t{0}, uint_max) == 0U);
// This is especially useful for signed types since rollover is undefined
assert(boost::int128::mul_sat(int_max, 2) == int_max);
assert(boost::int128::mul_sat(-(int_max - 2), 5) == std::numeric_limits<boost::int128::int128_t>::min());
// The only case in the library where div sat overflows is x = std::numeric_limits<int128_t>::min() and y = -1
assert(boost::int128::div_sat(std::numeric_limits<boost::int128::int128_t>::min(), -1) == int_max);
// Saturating case allows types to be safely converted without rollover behavior
assert(boost::int128::saturate_cast<boost::int128::int128_t>(uint_max) == int_max);
// You can also cast to builtin types
assert(boost::int128::saturate_cast<std::int64_t>(int_max) == INT64_MAX);
// Even of different signedness as this is treated like a static cast
assert(boost::int128::saturate_cast<std::int32_t>(uint_max) == INT32_MAX);
return 0;
}
Mixed Signedness Arithmetic
// #define BOOST_INT128_ALLOW_SIGN_CONVERSION
#include <boost/int128.hpp>
#include <cassert>
int main()
{
// By default, mixed type arithmetic is NOT ALLOWED
// In order for this file to compile #define BOOST_INT128_ALLOW_SIGN_CONVERSION
// BEFORE the inclusion of any file of this library (uncomment the top line)
//
// Unlike builtin types we cannot enforce sign correctness via compiler flag,
// so we made it the default.
constexpr boost::int128::uint128_t unsigned_value {3};
constexpr auto greater_unsigned_value {unsigned_value + 5};
assert(unsigned_value + 1 == 4);
assert(unsigned_value - 1 == 2);
assert(unsigned_value * 2 == 6);
assert(unsigned_value / 3 == 1);
assert(unsigned_value % 3 == 0);
assert(unsigned_value + 5 == greater_unsigned_value);
constexpr boost::int128::int128_t signed_value {-3};
assert(signed_value + 1U == -2);
assert(signed_value - 4U == -7);
assert(signed_value * 2 == -6);
assert(signed_value / 4U == 0);
return 0;
}
uint128_t
Benchmarks
Methodology
The benchmarks below represent the time in microseconds it takes to perform 20'000'000 operations between two values of random width (e.g. 2x1 words, 1x2 words, etc.).
On most platforms we use the builtin unsigned __int128
as the reference benchmark.
When this is unavailable (such as on 32-bit architectures) we us boost::multiprecision::uint128_t
(abbreviated as boost::mp::uint128_t
) as it is widely used, and known to be portable.
On MSVC platforms we use as reference std::_Unsigned128
from the header <__msvc_int128.hpp>
since this is bundled with their compiler.
Linux
x86_64
Operation | unsigned __int128 |
uint128_t |
boost::mp::uint128_t |
---|---|---|---|
Comparisons |
2068491 |
2416378 |
3191439 |
Addition |
383258 |
180522 |
545521 |
Subtraction |
208155 |
192633 |
420573 |
Multiplication |
291075 |
241174 |
675361 |
Division |
3953939 |
4246753 |
4976285 |
Modulo |
4042166 |
3953441 |
4478923 |

ARM64
Operation | unsigned __int128 |
uint128_t |
boost::mp::uint128_t |
---|---|---|---|
Comparisons |
3424403 |
2062167 |
5026689 |
Addition |
123659 |
133084 |
587373 |
Subtraction |
171721 |
99453 |
330052 |
Multiplication |
329287 |
283443 |
972009 |
Division |
2044821 |
1825020 |
2190856 |
Modulo |
2176318 |
1897933 |
2227961 |

S390x
Operation | unsigned __int128 |
uint128_t |
boost::mp::uint128_t |
---|---|---|---|
Comparisons |
14415854 |
12658246 |
16561079 |
Addition |
1232397 |
1423451 |
2909066 |
Subtraction |
1239808 |
775766 |
2744664 |
Multiplication |
1928533 |
2600663 |
2384775 |
Division |
8102813 |
5759377 |
7828137 |
Modulo |
9072599 |
6648180 |
9172574 |

PPC64LE
Operation | unsigned __int128 |
uint128_t |
boost::mp::uint128_t |
---|---|---|---|
Comparisons |
5242604 |
4450958 |
5704848 |
Addition |
221776 |
193063 |
847504 |
Subtraction |
222894 |
175259 |
786659 |
Multiplication |
194494 |
192929 |
795187 |
Division |
4821119 |
4896360 |
5344637 |
Modulo |
4955570 |
4273487 |
5407877 |

x86_32
Note
|
This platform has no hardware type so we compare relative to boost::mp::uint128_t
|
Operation | uint128_t |
boost::mp::uint128_t |
---|---|---|
Comparisons |
9000979 |
8722814 |
Addition |
898718 |
9912175 |
Subtraction |
778881 |
9773677 |
Multiplication |
1778273 |
8678420 |
Division |
8496503 |
18133965 |
Modulo |
9081442 |
11257837 |

ARM32
Note
|
This platform has no hardware type so we compare relative to boost::mp::uint128_t
|
Operation | uint128_t |
boost::mp::uint128_t |
---|---|---|
Comparisons |
5286033 |
4538707 |
Addition |
454715 |
5543856 |
Subtraction |
487190 |
6465126 |
Multiplication |
1471479 |
8246098 |
Division |
19868087 |
32820805 |
Modulo |
20332627 |
27238658 |

Windows
x86_64
Operation | std::_Unsigned128 |
uint128_t |
boost::mp::uint128_t |
---|---|---|---|
Comparisons |
2060556 |
1921174 |
3009890 |
Addition |
261475 |
106545 |
2710279 |
Subtraction |
178724 |
124181 |
3059187 |
Multiplication |
146063 |
136115 |
3495634 |
Division |
1332838 |
1360295 |
4852899 |
Modulo |
1465138 |
1471169 |
3926336 |

ARM64
Operation | std::_Unsigned128 |
uint128_t |
boost::mp::uint128_t |
---|---|---|---|
Comparisons |
3424403 |
2062167 |
5026689 |
Addition |
123659 |
133084 |
587373 |
Subtraction |
171721 |
99453 |
330052 |
Multiplication |
329287 |
283443 |
972009 |
Division |
2044821 |
1825020 |
2190856 |
Modulo |
2176318 |
1897933 |
2227961 |

x86_32
Operation | std::_Unsigned128 |
uint128_t |
boost::mp::uint128_t |
---|---|---|---|
Comparisons |
4215438 |
3883846 |
2852442 |
Addition |
199945 |
208436 |
3242910 |
Subtraction |
1206168 |
210874 |
3851129 |
Multiplication |
2282869 |
2680359 |
5378001 |
Division |
5516964 |
4328917 |
6948267 |
Modulo |
4551146 |
4330152 |
6294325 |

macOS
ARM64 (Apple Silicon)
Operation | unsigned __int128 |
uint128_t |
boost::mp::uint128_t |
---|---|---|---|
Comparisons |
131902 |
133564 |
134182 |
Addition |
20613 |
17912 |
40176 |
Subtraction |
20484 |
18237 |
40311 |
Multiplication |
20160 |
20580 |
43285 |
Division |
686521 |
699201 |
945928 |
Modulo |
777084 |
724648 |
953117 |

x86_64
Operation | unsigned __int128 |
uint128_t |
boost::mp::uint128_t |
---|---|---|---|
Comparisons |
131902 |
133564 |
134182 |
Addition |
20613 |
17912 |
40176 |
Subtraction |
20484 |
18237 |
40311 |
Multiplication |
20160 |
20580 |
43285 |
Division |
686521 |
699201 |
945928 |
Modulo |
777084 |
724648 |
953117 |

int128_t
Benchmarks
Methodology
The benchmarks below represent the time in microseconds it takes to perform 20'000'000 operations between two values of random width (e.g. 2x1 words, 1x2 words, etc.).
On most platforms we use the builtin __int128
as the reference benchmark.
When this is unavailable (such as on 32-bit architectures) we us boost::multiprecision::int128_t
(abbreviated as boost::mp::int128_t
) as it is widely used, and known to be portable.
On MSVC platforms we use as reference std::_Signed128
from the header <__msvc_int128.hpp>
since this is bundled with their compiler.
Linux
x86_64
Operation | __int128 |
int128_t |
boost::mp::int128_t |
---|---|---|---|
Comparisons |
2086545 |
1807917 |
6119951 |
Addition |
229772 |
249644 |
620566 |
Subtraction |
245300 |
172252 |
1522422 |
Multiplication |
403481 |
246659 |
898345 |
Division |
4315016 |
4369512 |
4767180 |
Modulo |
4303454 |
4686920 |
4855786 |

ARM64
Operation | __int128 |
int128_t |
boost::mp::int128_t |
---|---|---|---|
Comparisons |
3524205 |
2191692 |
5559916 |
Addition |
109691 |
126544 |
553814 |
Subtraction |
195129 |
196092 |
1024231 |
Multiplication |
286623 |
192214 |
924637 |
Division |
2350225 |
2163053 |
2718340 |
Modulo |
2345191 |
2167260 |
2380277 |

S390x
Operation | __int128 |
int128_t |
boost::mp::int128_t |
---|---|---|---|
Comparisons |
7033705 |
6231386 |
10322828 |
Addition |
558950 |
689575 |
1673032 |
Subtraction |
534362 |
329127 |
2149206 |
Multiplication |
911317 |
946090 |
1362947 |
Division |
4371582 |
3574992 |
3669927 |
Modulo |
4375939 |
3727994 |
4419901 |

PPC64LE
Operation | __int128 |
int128_t |
boost::mp::int128_t |
---|---|---|---|
Comparisons |
4538094 |
5796198 |
13907323 |
Addition |
221708 |
191841 |
1177034 |
Subtraction |
222629 |
174273 |
1861166 |
Multiplication |
193315 |
191785 |
878393 |
Division |
5607581 |
4669820 |
5616217 |
Modulo |
5623562 |
4750314 |
5641480 |

x86_32
Note
|
This platform has no hardware type so we compare relative to boost::mp::int128_t
|
Operation | int128_t |
boost::mp::int128_t |
---|---|---|
Comparisons |
9530060 |
12168353 |
Addition |
785799 |
7777469 |
Subtraction |
778881 |
8214089 |
Multiplication |
1148024 |
9477355 |
Division |
10337258 |
22857709 |
Modulo |
10438037 |
14848256 |

ARM32
Note
|
This platform has no hardware type so we compare relative to boost::mp::int128_t
|
Operation | int128_t |
boost::mp::int128_t |
---|---|---|
Comparisons |
6149439 |
6432579 |
Addition |
457850 |
5669571 |
Subtraction |
488321 |
7464427 |
Multiplication |
1793874 |
11410321 |
Division |
17738614 |
38956122 |
Modulo |
18064819 |
30144743 |

Windows
x86_64
Operation | std::_Signed128 |
int128_t |
boost::mp::int128_t |
---|---|---|---|
Comparisons |
2186843 |
2142626 |
4854983 |
Addition |
186771 |
184598 |
2645943 |
Subtraction |
193660 |
186335 |
2925784 |
Multiplication |
402806 |
117413 |
3887479 |
Division |
1612873 |
2369701 |
6437280 |
Modulo |
1637135 |
2218627 |
6236026 |

ARM64
Operation | std::_Signed128 |
int128_t |
boost::mp::int128_t |
---|---|---|---|
Comparisons |
911829 |
368104 |
2376802 |
Addition |
33233 |
34001 |
121700 |
Subtraction |
33411 |
34130 |
1488822 |
Multiplication |
117586 |
56324 |
1564799 |
Division |
1127267 |
1500725 |
2808293 |
Modulo |
1287100 |
1548073 |
2997474 |

x86_32
Operation | std::_Signed128 |
int128_t |
boost::mp::int128_t |
---|---|---|---|
Comparisons |
3187340 |
3046252 |
4269507 |
Addition |
185960 |
189165 |
2488618 |
Subtraction |
979025 |
192609 |
2783600 |
Multiplication |
1896082 |
3569921 |
4908622 |
Division |
5566403 |
4348306 |
6835035 |
Modulo |
4697289 |
4793845 |
6476032 |

macOS
ARM64 (Apple Silicon)
Operation | __int128 |
int128_t |
boost::mp::int128_t |
---|---|---|---|
Comparisons |
134803 |
144313 |
338665 |
Addition |
20133 |
17820 |
168326 |
Subtraction |
20156 |
17864 |
169666 |
Multiplication |
19974 |
18572 |
77514 |
Division |
649380 |
666749 |
962183 |
Modulo |
708247 |
681991 |
1014055 |

x86_64
Operation | __int128 |
int128_t |
boost::mp::int128_t |
---|---|---|---|
Comparisons |
1628142 |
1748005 |
4318109 |
Addition |
224648 |
180393 |
925013 |
Subtraction |
212849 |
131062 |
1876834 |
Multiplication |
432205 |
407829 |
651209 |
Division |
3924951 |
2409106 |
3719183 |
Modulo |
3042060 |
2423738 |
4443402 |

References
The following books, papers and blog posts serve as the basis for the algorithms used in the library:
-
Donald E. Knuth, The Art of Computer Programming Volume 2 Seminumerical Algorithms, 3rd edition, 1998
Copyright and License
This documentation is copyright 2025 Matt Borland, and is distributed under the Boost Software License, Version 1.0.