Int256: Portable and Performant 256-bit integers
Matt Borland
Description
Boost.Int256 is a fully portable implementation of 256-bit integers, both unsigned (uint256) and signed (int256).
The library supports a robust range of related functionality such as <bit>, <iostream>, <charconv>, and <format>.
On all platforms both types are exactly 256 bits wide.
For comparison, the maximum value of a 64-bit unsigned integer is 18,446,744,073,709,551,615, and the maximum value of a 128-bit unsigned integer is 340,282,366,920,938,463,463,374,607,431,768,211,455. The maximum value of a 256-bit unsigned integer is 78 decimal digits long:
115792089237316195423570985008687907853269984665640564039457584007913129639935
Values of this magnitude arise wherever a 256-bit hash, a curve scalar, or the exact product of two 128-bit numbers has to be treated as one integer rather than as a buffer of bytes.
The library is header-only, has no dependencies, and requires C++14 or later.
When using C++20 or later, the library can optionally be imported as a module: import boost.int256;.
Boost.Int256 is also available as a single header, which is regenerated from include/ by CI on every change.
Motivation
C++ provides no 256-bit integer type, and no mainstream compiler offers one as an extension the way GCC and Clang offer __int128 on 64-bit targets.
The usual substitute is a multiprecision integer, which solves the range problem at the cost of representation: Boost.Multiprecision uint256_t carries a limb count and a sign alongside the limbs, so it is larger than 32 bytes, is not trivially copyable, and cannot be handed to a GPU kernel or written straight into a packet.
Boost.Int256 provides a type that is exactly 32 bytes on every platform, holds four std::uint64_t words in a fixed order, is trivially copyable and standard layout, and is usable in constant expressions from C++14 onward.
Operations use compiler intrinsics and the compiler’s own 128-bit type as an intermediate where those are available, and optimized software implementations everywhere else, so results are identical on every target while performance follows the hardware.
The type and many of the functions also run unchanged in CUDA and SYCL device code.
Use Cases
-
Hashes as integers: SHA-256, Keccak-256, and BLAKE2s digests are 256 bits wide, so comparison, ordering, masking, and modular reduction become plain arithmetic instead of byte loops.
-
Curve and EVM arithmetic: secp256k1 and Ed25519 scalars, and the 256-bit word of the Ethereum Virtual Machine, are exactly the width of this type.
-
Exact 128x128 products: the full product of two 128-bit values, and the intermediate of a 128-bit modular multiply, need 256 bits and nothing more.
-
Wide fixed point and accumulators: a 256-bit accumulator holds an exact sum of many 64-bit or 128-bit products without a rounding step.
Supported Compilers
Boost.Int256 is tested natively on Ubuntu (x86_64, x86_32, s390x, aarch64, ARM32v7), macOS (x86_64, and Apple Silicon), and Windows (x86_64, x86_32, and ARM64); 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
-
NVCC 12.8 and later
Tested on Github Actions and Drone. Coverage can be found on Codecov.