Implementation:ClickHouse ClickHouse Wide Integer Impl
| Knowledge Sources | |
|---|---|
| Domains | Numeric_Types, Arithmetic |
| Last Updated | 2026-02-08 00:00 GMT |
Overview
A template-based implementation of arbitrary-width integers (128-bit, 256-bit, etc.) providing complete arithmetic operations for multi-precision integer calculations.
Description
This code implements the `wide::integer<Bits, Signed>` template class that provides integers wider than the native CPU word size. The implementation stores large integers as arrays of 64-bit base values and implements all arithmetic operations (addition, subtraction, multiplication, division), bitwise operations, shifts, and conversions. For 256-bit integers on x86-64, the code can leverage Clang's builtin `_BitInt(256)` type for improved performance. The class supports conversions from/to standard integer types, floating point, and provides specializations for 128-bit and 256-bit multiplication using native compiler `__int128` support. The implementation handles both signed and unsigned variants with proper two's complement arithmetic.
Usage
Use this when you need integer arithmetic beyond 64 bits, such as for precise decimal arithmetic in financial calculations, cryptographic operations, UUID manipulation, or exact integer math in scientific computing. The `UInt128` and `Int128` types are commonly used for storing very large counters, precise timestamps, or 128-bit identifiers. The `UInt256` and `Int256` types support even larger values for specialized applications.
Code Reference
Source Location
- Repository: ClickHouse
- File: base/base/wide_integer_impl.h
- Lines: 1-1664
Signature
namespace wide {
template <size_t Bits, typename Signed>
class integer {
public:
using base_type = uint64_t;
using signed_base_type = int64_t;
base_type items[Bits / (sizeof(base_type) * 8)];
// Constructors
constexpr integer() noexcept;
template <typename T> constexpr integer(T rhs) noexcept;
constexpr integer(const integer & other) = default;
// Assignment
template <typename T> constexpr integer & operator=(T rhs) noexcept;
// Arithmetic operators
template <typename T> constexpr auto operator+(const T & rhs) const noexcept;
template <typename T> constexpr auto operator-(const T & rhs) const noexcept;
template <typename T> constexpr auto operator*(const T & rhs) const noexcept;
template <typename T> constexpr auto operator/(const T & rhs) const;
template <typename T> constexpr auto operator%(const T & rhs) const;
// Compound assignment
template <typename T> constexpr integer & operator+=(const T & rhs) noexcept;
template <typename T> constexpr integer & operator-=(const T & rhs) noexcept;
template <typename T> constexpr integer & operator*=(const T & rhs) noexcept;
template <typename T> constexpr integer & operator/=(const T & rhs);
// Bitwise operators
constexpr integer operator~() const noexcept;
template <typename T> constexpr integer operator&(const T & rhs) const noexcept;
template <typename T> constexpr integer operator|(const T & rhs) const noexcept;
template <typename T> constexpr integer operator^(const T & rhs) const noexcept;
// Shifts
constexpr integer operator<<(unsigned shift) const noexcept;
constexpr integer operator>>(unsigned shift) const noexcept;
// Comparison
template <typename T> constexpr bool operator==(const T & rhs) const noexcept;
template <typename T> constexpr bool operator!=(const T & rhs) const noexcept;
template <typename T> constexpr bool operator<(const T & rhs) const noexcept;
template <typename T> constexpr bool operator>(const T & rhs) const noexcept;
// Unary operators
constexpr integer operator+() const noexcept;
constexpr integer operator-() const noexcept;
// Conversions
template <typename T> explicit constexpr operator T() const noexcept;
};
using Int128 = integer<128, signed>;
using UInt128 = integer<128, unsigned>;
using Int256 = integer<256, signed>;
using UInt256 = integer<256, unsigned>;
} // namespace wide
Import
#include <base/wide_integer_impl.h>
// Or use the type aliases:
#include <base/extended_types.h>
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| value | Integer, floating point, or wide integer | Yes | Value to construct from or assign |
| shift | unsigned | Yes | Bit shift amount for left/right shift operations |
| rhs | Integer or wide integer | Yes | Right-hand side operand for binary operations |
Outputs
| Name | Type | Description |
|---|---|---|
| result | wide::integer<Bits, Signed> | Result of arithmetic or bitwise operation |
| comparison | bool | Result of comparison operation |
| converted | Native type | Converted value from wide integer |
Usage Examples
#include <base/extended_types.h>
#include <iostream>
// Create 128-bit integers
UInt128 a = 1000000000000ULL;
UInt128 b = 2000000000000ULL;
// Arithmetic operations
UInt128 sum = a + b; // 3000000000000
UInt128 product = a * b; // Very large value
UInt128 difference = b - a; // 1000000000000
// Mixed-type arithmetic
UInt128 c = a + 500; // Automatic conversion
Int128 negative = -Int128(a); // Signed negation
// Bitwise operations
UInt128 mask = (UInt128(1) << 64) - 1; // Lower 64 bits set
UInt128 masked = product & mask;
// Comparison
if (a < b) {
// a is less than b
}
// Large constants
UInt128 large = UInt128(0xFFFFFFFFFFFFFFFFULL) << 64 | 0xFFFFFFFFFFFFFFFFULL;
// Represents 2^128 - 1
// 256-bit integers
UInt256 huge = UInt256(1) << 200; // 2^200
UInt256 enormous = huge * huge; // 2^400 (within 256-bit range)
// Conversion to native types
UInt128 value = 42;
uint64_t native = static_cast<uint64_t>(value); // 42
// Division and modulo
UInt128 dividend = 1000000;
UInt128 divisor = 37;
UInt128 quotient = dividend / divisor;
UInt128 remainder = dividend % divisor;
// Building large values
UInt128 high_bits = UInt128(12345) << 64;
UInt128 low_bits = 67890;
UInt128 combined = high_bits | low_bits;
// From floating point
double d = 1.23e20;
UInt128 from_double = UInt128(d);
// To floating point
UInt128 big_int = UInt128(1) << 100;
double as_double = static_cast<double>(big_int);
// Exact decimal arithmetic (financial)
Int128 cents = 12345; // $123.45 in cents
Int128 quantity = 1000;
Int128 total_cents = cents * quantity; // Exact multiplication
// Counter that won't overflow
UInt128 event_counter = 0;
for (int i = 0; i < 1000000; ++i) {
event_counter += i;
}
// Large timestamp (nanoseconds since epoch)
UInt128 nanoseconds = UInt128(std::time(nullptr)) * 1'000'000'000ULL;
// UUID (128-bit identifier)
struct UUID {
UInt128 value;
static UUID generate() {
// Generate random 128-bit value
return UUID{/* random UInt128 */};
}
};