From 0c5f4774b41afdc1a2194778f1a7102a8d9dfbe6 Mon Sep 17 00:00:00 2001 From: Andrew Slabko Date: Sat, 27 Jun 2026 12:50:35 +0200 Subject: [PATCH] Add native __int128 casts for bignum types Expose explicit conversions from Int128 and UInt128 to compiler-native 128-bit integers when `__int128` is available. Add unit coverage for signed and unsigned casts across zero, small values, large values, and boundary limits. --- clickhouse/types/bignum.h | 19 ++++++++++++++ ut/bignum_ut.cpp | 55 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/clickhouse/types/bignum.h b/clickhouse/types/bignum.h index a042fc2a..ca8aee62 100644 --- a/clickhouse/types/bignum.h +++ b/clickhouse/types/bignum.h @@ -4,6 +4,13 @@ #include #include + + #if defined(__SIZEOF_INT128__) + #define CH_CPP_HAS_INT128 1 + #else + #define CH_CPP_HAS_INT128 0 + #endif + /** * This file contains declarations and definitions of the types and API used for wide * (128-, 256- bit) integers. It first declares the Int128 and UInt128, implementation of which @@ -66,6 +73,12 @@ struct UInt128 { bool operator<=(const UInt128& other) const { return !(other < *this); } bool operator>=(const UInt128& other) const { return !(*this < other); } +#if CH_CPP_HAS_INT128 + explicit operator unsigned __int128() const { + return ((unsigned __int128)limbs[1] << 64 | limbs[0]); + } +#endif + // The value as two 64-bit limbs in little-endian order: // `limbs[0]` is the low 64 bits and `limbs[1]` is the high 64 bits uint64_t limbs[2]; @@ -104,6 +117,12 @@ struct Int128 { bool operator<=(const Int128& other) const { return !(other < *this); } bool operator>=(const Int128& other) const { return !(*this < other); } +#if CH_CPP_HAS_INT128 + explicit operator __int128() const { + return (__int128)((unsigned __int128)limbs[1] << 64 | limbs[0]); + } +#endif + // The value as two 64-bit limbs in little-endian order: // `limbs[0]` is the low 64 bits and `limbs[1]` is the high 64 bits uint64_t limbs[2]; diff --git a/ut/bignum_ut.cpp b/ut/bignum_ut.cpp index b8aaf975..2973ea69 100644 --- a/ut/bignum_ut.cpp +++ b/ut/bignum_ut.cpp @@ -2,6 +2,7 @@ #include #include +#include using clickhouse::Bignum; using clickhouse::Int128; @@ -135,3 +136,57 @@ TEST(BignumCompare, Int128Limits) { EXPECT_TRUE(min <= min); EXPECT_TRUE(max >= max); } + +#if CH_CPP_HAS_INT128 + +using i128 = __int128; +using u128 = unsigned __int128; + +constexpr u128 u128_max = ~u128{0}; +constexpr i128 i128_max = static_cast(u128_max >> 1); +constexpr i128 i128_min = -i128_max - 1; + +TEST(BignumCompare, Int128CastToNative) { + + struct TestSample { + std::string str; + i128 expect; + }; + + std::vector samples { + {"0", 0}, + {"-1", -1}, + {"1", 1}, + {"99999999999999999999999999999999999999", (i128)10000000000000000000UL * 10000000000000000000UL - 1}, + {"-99999999999999999999999999999999999999", -(i128)10000000000000000000UL * 10000000000000000000UL + 1}, + {"170141183460469231731687303715884105727", i128_max}, + {"-170141183460469231731687303715884105728", i128_min}, + }; + + for (size_t i = 0; i < samples.size(); ++i) { + auto x = Bignum::StringToInt128(samples[i].str); + EXPECT_TRUE(static_cast(x) == samples[i].expect); + } +} + +TEST(BignumCompare, UInt128CastToNative) { + + struct TestSample { + std::string str; + u128 expect; + }; + + std::vector samples { + {"0", 0}, + {"1", 1}, + {"99999999999999999999999999999999999999", (i128)10000000000000000000UL * 10000000000000000000UL - 1}, + {"170141183460469231731687303715884105727", (u128)i128_max}, + {"340282366920938463463374607431768211455", u128_max}, + }; + + for (size_t i = 0; i < samples.size(); ++i) { + auto x = Bignum::StringToUInt128(samples[i].str); + EXPECT_TRUE(static_cast(x) == samples[i].expect); + } +} +#endif