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

Literals

Functions

Listed by analogous STL header.

<cmath>

  • abs - Absolute Value

<iostream>

Enums

  • None

Constants

  • None

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

x64 Relative Performance

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

x64 Relative Performance

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

s390x Relative Performance

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

ppc64le Relative Performance

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

x86 Relative Performance

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

ARM32 Relative Performance

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

x64 Relative Performance

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

ARM64 Relative Performance

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

x86_32 Relative Performance

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

ARM64 Relative Performance

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

x64 Relative Performance

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

x64 Relative Performance

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

x64 Relative Performance

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

s390x Relative Performance

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

ppc64le Relative Performance

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

x86 Relative Performance

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

ARM32 Relative Performance

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

x64 Relative Performance

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

ARM64 Relative Performance

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

x86_32 Relative Performance

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

ARM64 Relative Performance

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

x64 Relative Performance

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

This documentation is copyright 2025 Matt Borland, and is distributed under the Boost Software License, Version 1.0.