int256

Description

int256 is a signed 256-bit integer.

#include <boost/int256.hpp>

namespace boost {
namespace int256 {

struct int256 {

    // words[0] is the least significant word on every platform; the sign is
    // bit 63 of words[3]
    std::uint64_t words[4] {};

    BOOST_INT256_HOST_DEVICE constexpr std::int64_t signed_high() const noexcept;

    // Constructors, conversion operators, and member operators
    // documented in detail below...
};

} // namespace int256
} // namespace boost

The type provides:

  • Constructors from all built-in integer types, from the four words (most significant first), and from the built-in floating-point types

  • Conversion operators to built-in integer and floating-point types

  • Full set of comparison operators (<, ⇐, >, >=, ==, !=), and <=> where the standard library provides it

  • Bitwise operators (~, |, &, ^, <<, >>)

  • Arithmetic operators (+, -, *, /, %)

  • Compound assignment variants of all binary operators

  • Increment and decrement operators (++, --)

  • Implicit, bit-copying conversion both ways with uint256; see Mixed Type Operations

int256 shares its layout with uint256: both are std::uint64_t words[4], and a value can be reinterpreted between the two types by a bit copy with no runtime cost.

Layout and Alignment

The single data member is an array of four std::uint64_t, and words[0] is the least significant word on every platform, including big-endian ones. The value is interpreted as two’s complement across all four words, and the sign is bit 63 of words[3]. As with uint256, the library does not reorder the words by endianness: there is no native 256-bit type to match layouts with, so a fixed order keeps one code path to test and keeps constant evaluation and run time identical.

static_assert(sizeof(int256) == 32, "");
static_assert(alignof(int256) == alignof(std::uint64_t), "");
static_assert(std::is_trivially_copyable<int256>::value, "");
static_assert(std::is_standard_layout<int256>::value, "");

The type is deliberately not over-aligned, for the same reasons as uint256: C++14 has no aligned operator new, so an over-aligned element type would make std::vector<int256> ill-behaved, and MSVC warns about over-aligned aggregates passed by value.

To read the top word as a signed quantity without relying on words[3]’s declared type, use `signed_high():

BOOST_INT256_HOST_DEVICE constexpr std::int64_t signed_high() const noexcept;

It is a pure reinterpretation of the stored bits and is constexpr on all platforms. Every sign-dependent operation in the library (comparisons, division, abs, text formatting, and so on) reads the sign through signed_high() rather than comparing words[3] directly.

Operator Behavior

All comparison, arithmetic, bitwise, and shift operators are defined between int256 and any built-in integer type, signed or unsigned, and, where the compiler provides them, against __int128 and unsigned __int128. The operators that the built-in integers allow with a floating-point operand, that is +, -, *, /, the six comparisons, <=>, and their compound assignment forms, are additionally defined between int256 and float, double, and long double. Specifically:

  • Every built-in integer, including unsigned __int128, fits inside the range of int256, so int256 op builtin always returns int256 and every comparison against a builtin is mathematically exact: there is no mixed sign trap the way there is between int256 and uint256.

  • int256 and uint256 follow the usual arithmetic conversions: both operands convert to uint256, so an arithmetic or bitwise result between them is uint256, not int256. For example, int256{-1} < uint256{1} is false, exactly as -1 < 1u is at 32 bits. See Mixed Type Operations for the full result-type table.

  • For shift operators, the result type follows the left operand: int256 << T and int256 >> T always return int256, regardless of T.

  • All comparison operators return bool, with the comparison performed on the operands after conversion to the common type.

  • Every compound assignment operator is also defined with the built-in integer on the left and the int256 on the right, in which case i op= v is i = static_cast<decltype(i)>(i op v). The one exception is int256& operator@=(int256&, const uint256&), which computes in the uint256 domain and converts the result back, matching what long long op= unsigned long long does. See Compound Assignment onto a Built-in Integer.

  • A floating-point operand is the common type: the int256 is converted to it first and the operation is performed in floating point, so the result of an arithmetic operator is the floating-point type, not int256. The modulo, bitwise, and shift operators reject a floating-point operand, as the built-in does. See Operations With Floating-Point Types.

All of `, `-`, `*`, `<<`, `+, --, and unary - wrap modulo 2256, computed in the unsigned domain, so there is no signed-overflow undefined behavior: std::numeric_limits<int256>::is_modulo is true.

Division or remainder by zero is undefined behavior, exactly as it is for the built-in integer types. The library performs no zero-divisor check, so the generated code matches a built-in division, and in a constant expression it is a hard compile-time error. BOOST_INT256_INT256_MIN / -1 is the one signed overflow the library defines rather than leaving undefined: it wraps to BOOST_INT256_INT256_MIN (and BOOST_INT256_INT256_MIN % -1 is 0), matching the two’s-complement wrap applied to the other overflowing signed operations. See Division and Modulo below.

See Mixed Type Operations for the full set of cross-type signatures and detailed result-type rules.

Constructors

namespace boost {
namespace int256 {

struct int256
{
    ...

    // Defaulted basic construction
    constexpr int256() noexcept = default;
    constexpr int256(const int256&) noexcept = default;
    constexpr int256(int256&&) noexcept = default;
    constexpr int256& operator=(const int256&) noexcept = default;
    constexpr int256& operator=(int256&&) noexcept = default;

    // Bit copy from uint256
    constexpr int256(const uint256& v) noexcept;

    // Construct from the four words, most significant first. The top word is
    // signed, so int256{-1, 0, 0, 0} works.
    BOOST_INT256_HOST_DEVICE constexpr int256(const std::int64_t w3, const std::uint64_t w2,
                                              const std::uint64_t w1, const std::uint64_t w0) noexcept;

    // Construct from integral types
    template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
    BOOST_INT256_HOST_DEVICE constexpr int256(const SignedInteger v) noexcept;

    template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
    BOOST_INT256_HOST_DEVICE constexpr int256(const UnsignedInteger v) noexcept;

    #if defined(BOOST_INT256_HAS_INT128) || defined(BOOST_INT256_HAS_MSVC_INT128)

    // Typically a typedef from __int128; sign-fills words 2 and 3
    BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR int256(const detail::builtin_i128 v) noexcept;

    // Typically a typedef from unsigned __int128; zero-fills words 2 and 3
    BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR int256(const detail::builtin_u128 v) noexcept;

    #endif

    // Boost.Int128's uint128 and int128, when <boost/int128.hpp> was included first;
    // behaves exactly as the builtin 128-bit constructors above
    template <typename Int128Type>
    BOOST_INT256_HOST_DEVICE constexpr int256(const Int128Type v) noexcept;

    // Construct from floating-point types
    template <BOOST_INT256_FLOATING_POINT_CONCEPT Float>
    BOOST_INT256_HOST_DEVICE constexpr int256(Float f) noexcept;
};

} // namespace int256
} // namespace boost

None of the constructors are marked explicit in order to match the implicit conversion behavior of the built-in integer types. A SignedInteger sign-fills words 1 through 3; an UnsignedInteger (including bool, which becomes 0 or 1) zero-fills them.

The word constructor takes its arguments most significant first, so int256{-1, 0, 0, 0} and int256{INT64_MIN, 0, 0, 0} are both well formed, and the former is -2192. There is no shorter word constructor; use the four-word form or an implicit conversion from a built-in type.

The floating-point constructor truncates toward zero, and for every value inside the range of int256 it produces exactly what a conversion to a 256-bit built-in signed type would produce. It is total instead of undefined outside that range, and saturates on every platform: NaN yields 0, values >= 2255 saturate to BOOST_INT256_INT256_MAX, and values ⇐ -2255 saturate to BOOST_INT256_INT256_MIN. Because the conversion truncates the magnitude toward zero and then applies the sign, it is symmetric: converting f and -f (for finite, non-zero f) produces values that are exact negatives of one another, and BOOST_INT256_INT256_MIN converts from -2255 exactly.

Conversions

namespace boost {
namespace int256 {

struct int256
{
    ...

    // Integer conversion operators
    BOOST_INT256_HOST_DEVICE explicit constexpr operator bool() const noexcept;

    template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
    BOOST_INT256_HOST_DEVICE constexpr operator SignedInteger() const noexcept;

    template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
    BOOST_INT256_HOST_DEVICE constexpr operator UnsignedInteger() const noexcept;

    #if defined(BOOST_INT256_HAS_INT128) || defined(BOOST_INT256_HAS_MSVC_INT128)

    BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR operator detail::builtin_i128() const noexcept;

    BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR operator detail::builtin_u128() const noexcept;

    #endif

    // Boost.Int128's uint128 and int128, when <boost/int128.hpp> was included first
    template <typename Int128Type>
    BOOST_INT256_HOST_DEVICE constexpr operator Int128Type() const noexcept;

    // Conversion to floating point
    BOOST_INT256_HOST_DEVICE constexpr operator float() const noexcept;
    BOOST_INT256_HOST_DEVICE constexpr operator double() const noexcept;
    constexpr operator long double() const noexcept; // There are no long doubles on device
};

} // namespace int256
} // namespace boost

All conversion operators except operator bool() are implicit to match the behavior of built-in integer types. operator bool() is explicit so that an int256 cannot accidentally bind to a bool parameter; contextual conversions (if (x), !x, and so on) still work. A conversion to a narrower integer keeps the low bits (words[0], reinterpreted with the target’s signedness), which is what static_cast between built-in integers does. A conversion to __int128 or unsigned __int128 keeps words[0] and words[1]. The same holds for Boost.Int128’s uint128 and int128; see Operations With Boost.Int128 Types for when those conversions exist and how to spell them portably.

Conversions to floating-point types go through the same symmetric, correctly rounded path as the floating-point constructor: the magnitude is converted and then the sign is applied, so BOOST_INT256_INT256_MIN converts to exactly -2255 in double. When the value does not fit the significand it is rounded once, to nearest with ties to even, and a value whose magnitude is above the range of the target type becomes an infinity of the matching sign.

int256 also converts to a C++23 <stdfloat> extended type (std::float16_t, float32_t, float64_t, float128_t, bfloat16_t) when the standard library provides it: host only, and only under C++23 or later on a toolchain whose standard library actually ships <stdfloat> with that specific type (GCC 13 and later with libstdc; as of this writing, libc and MSVC’s standard library do not yet provide it, so those types are simply absent from int256 there, not merely disabled). The conversion is correctly rounded exactly as above, so the narrowest of these, std::float16_t (8-bit significand, no value above 65504 in magnitude), overflows to an infinity of the matching sign for any int256 past that point.

Under -ffast-math, -ffinite-math-only, or the Intel compiler’s default -fp-model=fast, the compiler may assume no floating-point operand is ever NaN or infinity, so a conversion from one of those values gives unspecified results instead of the behavior described above. Use -fp-model=precise with icpx when NaN or infinity handling matters. This was found in CI, where icpx’s default model turned `uint256{+inf} into 2^191.

Comparison Operators

Less Than

BOOST_INT256_HOST_DEVICE constexpr bool operator<(const int256& lhs, const int256& rhs) noexcept;

template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
BOOST_INT256_HOST_DEVICE constexpr bool operator<(const int256& lhs, const SignedInteger rhs) noexcept;

template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
BOOST_INT256_HOST_DEVICE constexpr bool operator<(const SignedInteger lhs, const int256& rhs) noexcept;

template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
BOOST_INT256_HOST_DEVICE constexpr bool operator<(const int256& lhs, const UnsignedInteger rhs) noexcept;

template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
BOOST_INT256_HOST_DEVICE constexpr bool operator<(const UnsignedInteger lhs, const int256& rhs) noexcept;

// And, where the compiler provides them, the four builtin 128-bit flavors
#if defined(BOOST_INT256_HAS_INT128) || defined(BOOST_INT256_HAS_MSVC_INT128)
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR bool operator<(const int256& lhs, const detail::builtin_u128 rhs) noexcept;
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR bool operator<(const detail::builtin_u128 lhs, const int256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR bool operator<(const int256& lhs, const detail::builtin_i128 rhs) noexcept;
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR bool operator<(const detail::builtin_i128 lhs, const int256& rhs) noexcept;
#endif

// And, against uint256
BOOST_INT256_HOST_DEVICE constexpr bool operator<(const int256& lhs, const uint256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE constexpr bool operator<(const uint256& lhs, const int256& rhs) noexcept;

Returns whether the lhs value is less than the rhs value without exception. Against a builtin integer the comparison is mathematically exact. Against a uint256 both operands convert to uint256 first; see Mixed Type Operations. A floating-point right or left operand is also accepted, in which case the result follows Operations With Floating-Point Types.

Less Than or Equal To, Greater Than, Greater Than or Equal To, Equality, Inequality

<=, >, >=, ==, and != follow exactly the same set of flavors as < above, each returning bool without exception. == and != additionally accept a bool operand on either side.

Spaceship Operator (Requires C++20)

BOOST_INT256_HOST_DEVICE constexpr std::strong_ordering operator<=>(const int256& lhs, const int256& rhs) noexcept;

template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
BOOST_INT256_HOST_DEVICE constexpr std::strong_ordering operator<=>(const int256& lhs, const SignedInteger rhs) noexcept;

template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
BOOST_INT256_HOST_DEVICE constexpr std::strong_ordering operator<=>(const int256& lhs, const UnsignedInteger rhs) noexcept;

BOOST_INT256_HOST_DEVICE constexpr std::strong_ordering operator<=>(const int256& lhs, const uint256& rhs) noexcept;

template <BOOST_INT256_FLOATING_POINT_CONCEPT Float>
BOOST_INT256_HOST_DEVICE constexpr std::partial_ordering operator<=>(const int256& lhs, const Float rhs) noexcept;

Available when BOOST_INT256_HAS_SPACESHIP_OPERATOR is defined, which requires C++20 and a standard library that provides <compare>. Against an integer or uint256 operand it returns one of the following without exception:

  • std::strong_ordering::less if lhs < rhs

  • std::strong_ordering::equivalent if lhs == rhs

  • std::strong_ordering::greater otherwise (implies lhs > rhs)

A floating-point operand yields a std::partial_ordering, which is unordered when that operand is NaN.

Bitwise Operators

Complement

BOOST_INT256_HOST_DEVICE constexpr int256 operator~(const int256& rhs) noexcept;

Returns the bitwise complement of rhs without exception.

Or, And, Xor

BOOST_INT256_HOST_DEVICE constexpr int256 operator|(const int256& lhs, const int256& rhs) noexcept;

template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator|(const int256& lhs, const SignedInteger rhs) noexcept;

template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator|(const SignedInteger lhs, const int256& rhs) noexcept;

template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator|(const int256& lhs, const UnsignedInteger rhs) noexcept;

template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator|(const UnsignedInteger lhs, const int256& rhs) noexcept;

// And, where the compiler provides them, the four builtin 128-bit flavors
#if defined(BOOST_INT256_HAS_INT128) || defined(BOOST_INT256_HAS_MSVC_INT128)
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR int256 operator|(const int256& lhs, const detail::builtin_u128 rhs) noexcept;
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR int256 operator|(const detail::builtin_u128 lhs, const int256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR int256 operator|(const int256& lhs, const detail::builtin_i128 rhs) noexcept;
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR int256 operator|(const detail::builtin_i128 lhs, const int256& rhs) noexcept;
#endif

// And, against uint256 (result is uint256)
BOOST_INT256_HOST_DEVICE constexpr uint256 operator|(const int256& lhs, const uint256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE constexpr uint256 operator|(const uint256& lhs, const int256& rhs) noexcept;

operator& and operator^ are declared with exactly the same set of flavors. Each returns the bitwise or, and, or xor of lhs and rhs without exception. A signed builtin operand contributes a sign-filled word; the result against a uint256 operand is uint256.

Left Shift

template <BOOST_INT256_INTEGER_CONCEPT Integer>
BOOST_INT256_HOST_DEVICE constexpr int256 operator<<(const int256& lhs, const Integer rhs) noexcept;

BOOST_INT256_HOST_DEVICE constexpr int256 operator<<(const int256& lhs, const int256& rhs) noexcept;

// The left operand's type is preserved against a uint256 shift count
BOOST_INT256_HOST_DEVICE constexpr int256 operator<<(const int256& lhs, const uint256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE constexpr uint256 operator<<(const uint256& lhs, const int256& rhs) noexcept;

// A built-in integer on the left keeps its own type after integral promotion
template <BOOST_INT256_INTEGER_CONCEPT Integer>
BOOST_INT256_HOST_DEVICE constexpr detail::promoted_t<Integer> operator<<(const Integer lhs, const int256& rhs) noexcept;

// When the compiler provides native 128-bit integers, a built-in 128-bit lhs
// shifted by an int256 returns that built-in type.
#if defined(BOOST_INT256_HAS_INT128) || defined(BOOST_INT256_HAS_MSVC_INT128)
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR detail::builtin_u128 operator<<(const detail::builtin_u128 lhs, const int256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR detail::builtin_i128 operator<<(const detail::builtin_i128 lhs, const int256& rhs) noexcept;
#endif

Returns the bitwise left shift of lhs without exception. The shift is logical and computed in the unsigned domain regardless of the sign of lhs: the low bits vacated by the shift are filled with zero and bits shifted past bit 255 are discarded, so the value wraps modulo 2256. The shift count is the integer value of the right operand; when the right operand is a 256-bit type only its low word is read.

Shifting by a count that is negative or greater than or equal to 256 (the operand width in bits) is undefined behavior, exactly as for the built-in shift operators ([expr.shift] in the C++ standard). No result is guaranteed, not even zero, and inside a constant expression the program is ill-formed.

Right Shift

template <BOOST_INT256_INTEGER_CONCEPT Integer>
BOOST_INT256_HOST_DEVICE constexpr int256 operator>>(const int256& lhs, const Integer rhs) noexcept;

BOOST_INT256_HOST_DEVICE constexpr int256 operator>>(const int256& lhs, const int256& rhs) noexcept;

BOOST_INT256_HOST_DEVICE constexpr int256 operator>>(const int256& lhs, const uint256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE constexpr uint256 operator>>(const uint256& lhs, const int256& rhs) noexcept;

template <BOOST_INT256_INTEGER_CONCEPT Integer>
BOOST_INT256_HOST_DEVICE constexpr detail::promoted_t<Integer> operator>>(const Integer lhs, const int256& rhs) noexcept;

#if defined(BOOST_INT256_HAS_INT128) || defined(BOOST_INT256_HAS_MSVC_INT128)
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR detail::builtin_u128 operator>>(const detail::builtin_u128 lhs, const int256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR detail::builtin_i128 operator>>(const detail::builtin_i128 lhs, const int256& rhs) noexcept;
#endif

Returns the bitwise right shift of lhs without exception. Unlike the left shift, the right shift is arithmetic: the high bits vacated by the shift are filled with copies of the sign bit, so -1 >> 255 is -1 and the operation matches dividing by a power of two and rounding toward negative infinity.

Shifting by a count that is negative or greater than or equal to 256 is undefined behavior, exactly as for the built-in shift operators. No result is guaranteed, not even a fully sign-extended value, and inside a constant expression the program is ill-formed.

Arithmetic Operators

All of the arithmetic operators (except division and remainder) use two’s-complement wrap-around on overflow, computed in the unsigned domain so that no operation triggers signed-overflow undefined behavior. When a result is not representable in an int256 it silently rolls over modulo 2256 rather than trapping, matching std::numeric_limits<int256>::is_modulo. For example, BOOST_INT256_INT256_MAX + 1 wraps to BOOST_INT256_INT256_MIN, and BOOST_INT256_INT256_MIN - 1 wraps to BOOST_INT256_INT256_MAX.

Unary Plus and Minus

BOOST_INT256_HOST_DEVICE constexpr int256 operator+(const int256& value) noexcept;
BOOST_INT256_HOST_DEVICE constexpr int256 operator-(const int256& value) noexcept;

Unary operator+ returns value unchanged. Unary operator- returns 0 - value, computed in the unsigned domain, wrapping modulo 2256. Negating BOOST_INT256_INT256_MIN wraps back to itself, matching the behavior of built-in signed integers.

Addition

BOOST_INT256_HOST_DEVICE constexpr int256 operator+(const int256& lhs, const int256& rhs) noexcept;

template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator+(const int256& lhs, const SignedInteger rhs) noexcept;

template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator+(const SignedInteger lhs, const int256& rhs) noexcept;

template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator+(const int256& lhs, const UnsignedInteger rhs) noexcept;

template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator+(const UnsignedInteger lhs, const int256& rhs) noexcept;

// And, where the compiler provides them, the four builtin 128-bit flavors
#if defined(BOOST_INT256_HAS_INT128) || defined(BOOST_INT256_HAS_MSVC_INT128)
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR int256 operator+(const int256& lhs, const detail::builtin_u128 rhs) noexcept;
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR int256 operator+(const detail::builtin_u128 lhs, const int256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR int256 operator+(const int256& lhs, const detail::builtin_i128 rhs) noexcept;
BOOST_INT256_HOST_DEVICE BOOST_INT256_BUILTIN_CONSTEXPR int256 operator+(const detail::builtin_i128 lhs, const int256& rhs) noexcept;
#endif

// And, against uint256 (result is uint256)
BOOST_INT256_HOST_DEVICE constexpr uint256 operator+(const int256& lhs, const uint256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE constexpr uint256 operator+(const uint256& lhs, const int256& rhs) noexcept;

Returns as an int256 the sum of lhs and rhs, computed in the unsigned domain and wrapping on overflow. operator- and operator* are declared with exactly the same set of flavors, and wrap the same way. This operation is subject to the mixed sign rules discussed above. A floating-point right or left operand is also accepted, in which case the result follows Operations With Floating-Point Types.

Division

BOOST_INT256_HOST_DEVICE constexpr int256 operator/(const int256& lhs, const int256& rhs) noexcept;

template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator/(const int256& lhs, const SignedInteger rhs) noexcept;

template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator/(const SignedInteger lhs, const int256& rhs) noexcept;

template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator/(const int256& lhs, const UnsignedInteger rhs) noexcept;

template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator/(const UnsignedInteger lhs, const int256& rhs) noexcept;

// And, against uint256 (result is uint256)
BOOST_INT256_HOST_DEVICE constexpr uint256 operator/(const int256& lhs, const uint256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE constexpr uint256 operator/(const uint256& lhs, const int256& rhs) noexcept;

Returns as an int256 the quotient of lhs and rhs without exception. The division works on the uint256 magnitudes of the operands: abs(lhs) and abs(rhs) are computed (abs of BOOST_INT256_INT256_MIN has the exact bit pattern 2255, so this is always well defined), the magnitudes are divided as uint256, and the quotient’s sign is negative exactly when lhs and rhs have different signs. The quotient truncates toward zero, so for any rhs other than 0 (and other than the overflow noted below) the result is identical to the built-in signed division. Division by zero is undefined behavior: the library adds no zero-divisor check, and a zero divisor seen during constant evaluation is a hard compile-time error. BOOST_INT256_INT256_MIN / -1 is defined to wrap to BOOST_INT256_INT256_MIN rather than being left undefined. A mixed builtin dividend divided by an int256 divisor of BOOST_INT256_INT256_MIN is exact: the quotient is 0 and the remainder equals the dividend, for every builtin dividend (this is the case a naive "negate and compare magnitudes" implementation gets wrong, because negating BOOST_INT256_INT256_MIN overflows). This operation is subject to the mixed sign rules discussed above. A floating-point right or left operand is also accepted, in which case the result follows Operations With Floating-Point Types.

Modulo

BOOST_INT256_HOST_DEVICE constexpr int256 operator%(const int256& lhs, const int256& rhs) noexcept;

template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator%(const int256& lhs, const SignedInteger rhs) noexcept;

template <BOOST_INT256_SIGNED_INTEGER_CONCEPT SignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator%(const SignedInteger lhs, const int256& rhs) noexcept;

template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator%(const int256& lhs, const UnsignedInteger rhs) noexcept;

template <BOOST_INT256_UNSIGNED_INTEGER_CONCEPT UnsignedInteger>
BOOST_INT256_HOST_DEVICE constexpr int256 operator%(const UnsignedInteger lhs, const int256& rhs) noexcept;

// And, against uint256 (result is uint256)
BOOST_INT256_HOST_DEVICE constexpr uint256 operator%(const int256& lhs, const uint256& rhs) noexcept;
BOOST_INT256_HOST_DEVICE constexpr uint256 operator%(const uint256& lhs, const int256& rhs) noexcept;

Returns as an int256 the remainder of lhs and rhs without exception. The remainder takes the sign of the dividend lhs, and for any rhs other than 0 the result is identical to the built-in signed remainder, so that (lhs / rhs) * rhs + lhs % rhs == lhs. Remainder by zero is undefined behavior, on the same terms as division above. BOOST_INT256_INT256_MIN % -1 is defined to be 0 rather than being left undefined. A mixed builtin dividend taken modulo an int256 divisor of BOOST_INT256_INT256_MIN is exact: the remainder equals the dividend (this is the int128 defect that this library does not repeat; see the release notes). This operator is only defined for integers, matching the built-in types: a floating-point operand is rejected. It is subject to the mixed sign rules discussed above.

Compound Assignment

struct int256
{
    ...

    // For each of |= &= ^= <<= >>= += -= *= /= %=
    template <BOOST_INT256_INTEGER_CONCEPT Integer>
    BOOST_INT256_HOST_DEVICE constexpr int256& operator+=(Integer rhs) noexcept;

    BOOST_INT256_HOST_DEVICE constexpr int256& operator+=(const int256& rhs) noexcept;

    // Computed in the uint256 domain and converted back
    BOOST_INT256_HOST_DEVICE constexpr int256& operator+=(const uint256& rhs) noexcept;

    // The floating-point forms are defined for += -= *= /= and deleted for the rest
    template <BOOST_INT256_FLOATING_POINT_CONCEPT Float>
    BOOST_INT256_HOST_DEVICE constexpr int256& operator+=(Float rhs) noexcept;

    // Increment and decrement
    BOOST_INT256_HOST_DEVICE constexpr int256& operator++() noexcept;
    BOOST_INT256_HOST_DEVICE constexpr int256 operator++(int) noexcept;
    BOOST_INT256_HOST_DEVICE constexpr int256& operator--() noexcept;
    BOOST_INT256_HOST_DEVICE constexpr int256 operator--(int) noexcept;
};

Every compound assignment operator onto a builtin integer or int256 right operand is *this = *this op rhs, so it inherits the behavior of the corresponding binary operator. The const uint256& overload is the one place int256’s compound assignment differs from a plain `*this = *this op rhs: int256& operator/=(int256&, const uint256&) (and the other nine) compute the operation in the uint256 domain and convert the result back to int256, matching what long long /= unsigned long long does at 64 bits. This is a deliberate correction relative to Boost.Int128’s int128 /= uint128, which resolves to the member int128 op=(int128) overload and therefore divides signed where the mixed-type builtin operation divides unsigned; see the release notes. A floating-point right operand converts the value to that type, applies the operation, and converts the result back; %=, &=, |=, ^=, <<= and >>= with a floating-point operand are declared deleted rather than left to the implicit conversions. The same operators are also available with a built-in integer on the left and an int256 on the right; see Compound Assignment onto a Built-in Integer.

Absolute Value

BOOST_INT256_HOST_DEVICE constexpr int256 abs(const int256& value) noexcept;

Returns the absolute value of value as an int256, without exception. The implementation is branch-free: it builds a mask of all-ones or all-zeros from the sign of value and computes (value ^ mask) - mask, so it never inspects the sign with a conditional. It is defined after operator^ and operator- in the header, because abs is not a template and an earlier definition would make overload lookup for those operators ambiguous.

For negative inputs the result is computed by negation in the unsigned domain, so the range of the return value is the same asymmetric range as the type itself. The magnitude of BOOST_INT256_INT256_MIN is one greater than BOOST_INT256_INT256_MAX and is therefore not representable as a positive int256. In that single case abs rolls over and returns BOOST_INT256_INT256_MIN unchanged, so the result remains negative; the underlying bit pattern is still 2255, which is exactly what the division and remainder operators above rely on. This is consistent with the two’s-complement wrap-around of unary operator- described above. Note that calling std::abs on a built-in signed integer holding its minimum value is undefined behavior, whereas this overload is well-defined for every input and never traps or throws.

<limits> Support and Values

A full specialization of std::numeric_limits<int256> is provided. It is defined in <boost/int256/limits.hpp> and is also included by the umbrella header <boost/int256.hpp>. The member values match those of a built-in signed 256-bit integer.

#include <boost/int256/limits.hpp>

namespace std {

template <>
class numeric_limits<boost::int256::int256>;

} // namespace std

Member Functions

Each function is static constexpr and returns an int256.

Function Value

(min)()

-57896044618658097711785492504343953926634992332820282019728792003956564819968 (-2255)

lowest()

-57896044618658097711785492504343953926634992332820282019728792003956564819968 (-2255)

(max)()

57896044618658097711785492504343953926634992332820282019728792003956564819967 (2255 - 1)

epsilon()

0

round_error()

0

infinity()

0

quiet_NaN()

0

signaling_NaN()

0

denorm_min()

0

Wrap min and max in parentheses at the call site, as (std::numeric_limits<int256>::min)(), so the call is not intercepted by the min/max function-like macros that some platform headers define.

Member Constants

Constant Value

is_specialized

true

is_signed

true

is_integer

true

is_exact

true

is_bounded

true

is_modulo

true

is_iec559

false

has_infinity

false

has_quiet_NaN

false

has_signaling_NaN

false

has_denorm

std::denorm_absent

has_denorm_loss

false

round_style

std::round_toward_zero

digits

255

digits10

76

max_digits10

0

radix

2

min_exponent

0

min_exponent10

0

max_exponent

0

max_exponent10

0

traps

std::numeric_limits<std::uint64_t>::traps

tinyness_before

false

digits10 is 76 because 1076 is the largest power of ten every int256 (positive or negative) is guaranteed to hold, while the maximum itself is 77 decimal digits long.

The equivalent C-style constant macros are available in <boost/int256/climits.hpp>: BOOST_INT256_INT256_MIN and BOOST_INT256_INT256_MAX.