18#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
19 #define LIGHTWEIGHT_HAVE_NATIVE_INT128 1
52 std::uint64_t high {};
55 constexpr Int128Soft() noexcept = default;
59 constexpr Int128Soft(std::int64_t value) noexcept:
60 low {
static_cast<std::uint64_t
>(value) },
61 high { value < 0 ? ~std::uint64_t { 0 } : std::uint64_t { 0 } }
67 constexpr Int128Soft(std::uint64_t value)
noexcept:
75 constexpr Int128Soft(std::uint64_t lowWord, std::uint64_t highWord)
noexcept:
89 template <std::
floating_po
int T>
90 constexpr explicit Int128Soft(T value)
noexcept
92 if (!(value == value))
95 auto const negative = value < T { 0 };
96 auto magnitude = negative ? -value : value;
101 auto limit = T { 1 };
102 for (
auto bit = 0; bit != 127; ++bit)
105 if (magnitude >= limit)
108 low = negative ? std::uint64_t { 0 } : ~std::uint64_t { 0 };
109 high = negative ? (std::uint64_t { 1 } << 63) : (~std::uint64_t { 0 } >> 1);
116 auto divisor = T { 1 };
117 for (
auto bit = 0; bit != 64; ++bit)
120 auto const highPart = std::trunc(magnitude / divisor);
121 auto const lowPart = std::trunc(magnitude - (highPart * divisor));
123 low =
static_cast<std::uint64_t
>(lowPart);
124 high =
static_cast<std::uint64_t
>(highPart);
131 [[nodiscard]]
constexpr bool IsNegative() const noexcept
133 return (high >> 63) != 0;
138 [[nodiscard]]
constexpr Int128Soft operator-() const noexcept
140 auto const invertedLow = ~low;
141 auto const invertedHigh = ~high;
142 auto const carriedLow = invertedLow + 1;
143 auto const carriedHigh = invertedHigh + (carriedLow < invertedLow ? 1 : 0);
144 return { carriedLow, carriedHigh };
148 [[nodiscard]]
constexpr Int128Soft operator+() const noexcept
160 template <std::
floating_po
int T>
161 [[nodiscard]]
constexpr T To() const noexcept
163 auto const negative = IsNegative();
164 auto const magnitude = negative ? -*this : *
this;
166 auto scale = T { 1 };
167 for (
auto bit = 0; bit != 64; ++bit)
170 auto const result = (
static_cast<T
>(magnitude.high) * scale) +
static_cast<T
>(magnitude.low);
171 return negative ? -result : result;
175 [[nodiscard]]
constexpr explicit operator float() const noexcept
181 [[nodiscard]]
constexpr explicit operator double() const noexcept
187 [[nodiscard]]
constexpr explicit operator long double() const noexcept
189 return To<long double>();
194 [[nodiscard]]
constexpr explicit operator std::int64_t() const noexcept
196 return static_cast<std::int64_t
>(low);
201 [[nodiscard]]
constexpr explicit operator std::uint64_t() const noexcept
207 [[nodiscard]]
constexpr explicit operator std::int32_t() const noexcept
209 return static_cast<std::int32_t
>(low);
213 [[nodiscard]]
constexpr bool operator==(Int128Soft
const& other)
const noexcept =
default;
218 [[nodiscard]]
constexpr std::strong_ordering operator<=>(Int128Soft
const& other)
const noexcept
220 if (
auto const negative = IsNegative(); negative != other.IsNegative())
221 return negative ? std::strong_ordering::less : std::strong_ordering::greater;
223 if (high != other.high)
224 return high <=> other.high;
226 return low <=> other.low;
237 [[nodiscard]]
inline std::string ToDecimalString(Int128Soft value)
noexcept
239 if (value == Int128Soft {})
242 auto const negative = value.IsNegative();
247 auto const magnitude = negative ? -value : value;
248 auto low = magnitude.low;
249 auto high = magnitude.high;
256 constexpr auto chunkDivisor = std::uint64_t { 1'000'000'000ULL };
257 constexpr auto chunkDigits = 9;
259 auto chunks = std::string {};
261 while (high != 0 || low != 0)
263 auto remainder = std::uint64_t { 0 };
265 auto const limbs = std::array<std::uint32_t, 4> {
266 static_cast<std::uint32_t
>(high >> 32),
267 static_cast<std::uint32_t
>(high & 0xFFFF'FFFFULL),
268 static_cast<std::uint32_t
>(low >> 32),
269 static_cast<std::uint32_t
>(low & 0xFFFF'FFFFULL),
272 auto quotientLimbs = std::array<std::uint32_t, 4> {};
274 for (
auto index = std::size_t { 0 }; index != limbs.size(); ++index)
277 auto const dividend = (remainder << 32) | limbs[index];
278 quotientLimbs[index] =
static_cast<std::uint32_t
>(dividend / chunkDivisor);
279 remainder = dividend % chunkDivisor;
282 high = (
static_cast<std::uint64_t
>(quotientLimbs[0]) << 32) | quotientLimbs[1];
283 low = (
static_cast<std::uint64_t
>(quotientLimbs[2]) << 32) | quotientLimbs[3];
287 for (
auto digit = 0; digit != chunkDigits; ++digit)
289 chunks.push_back(
static_cast<char>(
'0' +
static_cast<char>(remainder % 10)));
295 while (chunks.size() > 1 && chunks.back() ==
'0')
299 chunks.push_back(
'-');
301 return std::string { chunks.rbegin(), chunks.rend() };
313#if defined(LIGHTWEIGHT_HAVE_NATIVE_INT128)
314using Int128 = __int128_t;
316using Int128 = detail::Int128Soft;
319static_assert(
sizeof(Int128) == 16,
"The unscaled carrier must be exactly as wide as SQL_NUMERIC_STRUCT::val.");
330 [[nodiscard]]
inline std::string Int128ToString(Int128 value)
noexcept
332#if defined(LIGHTWEIGHT_HAVE_NATIVE_INT128)
333 auto const negative = value < 0;
334 auto magnitude =
static_cast<__uint128_t
>(negative ? -value : value);
339 auto reversed = std::string {};
340 while (magnitude != 0)
342 reversed.push_back(
static_cast<char>(
'0' +
static_cast<char>(magnitude % 10)));
346 reversed.push_back(
'-');
348 return std::string { reversed.rbegin(), reversed.rend() };
350 return ToDecimalString(value);