#pragma once // Moved Decimal-related functions out from Core/Types.h to reduce compilation time. #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int DECIMAL_OVERFLOW; } namespace DecimalUtils { static constexpr size_t minPrecision() { return 1; } template static constexpr size_t maxPrecision() { return 0; } template <> constexpr size_t maxPrecision() { return 9; } template <> constexpr size_t maxPrecision() { return 18; } template <> constexpr size_t maxPrecision() { return 38; } template <> constexpr size_t maxPrecision() { return 76; } template inline auto scaleMultiplier(UInt32 scale) { if constexpr (std::is_same_v || std::is_same_v) return common::exp10_i32(scale); else if constexpr (std::is_same_v || std::is_same_v) return common::exp10_i64(scale); else if constexpr (std::is_same_v || std::is_same_v) return common::exp10_i128(scale); else if constexpr (std::is_same_v || std::is_same_v) return common::exp10_i256(scale); } /** Components of DecimalX value: * whole - represents whole part of decimal, can be negative or positive. * fractional - for fractional part of decimal, always positive. */ template struct DecimalComponents { T whole; T fractional; }; /** Make a decimal value from whole and fractional components with given scale multiplier. * where scale_multiplier = scaleMultiplier(scale) * this is to reduce number of calls to scaleMultiplier when scale is known. * * Sign of `whole` controls sign of result: negative whole => negative result, positive whole => positive result. * Sign of `fractional` is expected to be positive, otherwise result is undefined. * If `scale` is to big (scale > maxPrecision), result is undefined. */ template inline DecimalType decimalFromComponentsWithMultiplier( const typename DecimalType::NativeType & whole, const typename DecimalType::NativeType & fractional, typename DecimalType::NativeType scale_multiplier) { using T = typename DecimalType::NativeType; const auto fractional_sign = whole < 0 ? -1 : 1; T whole_scaled = 0; if (common::mulOverflow(whole, scale_multiplier, whole_scaled)) throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW); const T value = whole_scaled + fractional_sign * (fractional % scale_multiplier); return DecimalType(value); } /** Make a decimal value from whole and fractional components with given scale. * * @see `decimalFromComponentsWithMultiplier` for details. */ template inline DecimalType decimalFromComponents( const typename DecimalType::NativeType & whole, const typename DecimalType::NativeType & fractional, UInt32 scale) { using T = typename DecimalType::NativeType; return decimalFromComponentsWithMultiplier(whole, fractional, scaleMultiplier(scale)); } /** Make a decimal value from whole and fractional components with given scale. * @see `decimalFromComponentsWithMultiplier` for details. */ template inline DecimalType decimalFromComponents( const DecimalComponents & components, UInt32 scale) { return decimalFromComponents(components.whole, components.fractional, scale); } /** Split decimal into whole and fractional parts with given scale_multiplier. * This is an optimization to reduce number of calls to scaleMultiplier on known scale. */ template inline DecimalComponents splitWithScaleMultiplier( const DecimalType & decimal, typename DecimalType::NativeType scale_multiplier) { using T = typename DecimalType::NativeType; const auto whole = decimal.value / scale_multiplier; auto fractional = decimal.value % scale_multiplier; if (fractional < T(0)) fractional *= T(-1); return {whole, fractional}; } /// Split decimal into components: whole and fractional part, @see `DecimalComponents` for details. template inline DecimalComponents split(const DecimalType & decimal, UInt32 scale) { if (scale == 0) { return {decimal.value, 0}; } return splitWithScaleMultiplier(decimal, scaleMultiplier(scale)); } /** Get whole part from decimal. * * Sign of result follows sign of `decimal` value. * If scale is to big, result is undefined. */ template inline typename DecimalType::NativeType getWholePart(const DecimalType & decimal, size_t scale) { if (scale == 0) return decimal.value; return decimal.value / scaleMultiplier(scale); } template inline typename DecimalType::NativeType getFractionalPartWithScaleMultiplier( const DecimalType & decimal, typename DecimalType::NativeType scale_multiplier) { using T = typename DecimalType::NativeType; T result = decimal.value; if constexpr (!keep_sign) if (result < T(0)) result = -result; return result % scale_multiplier; } /** Get fractional part from decimal * * Result is always positive. * If scale is to big, result is undefined. */ template inline typename DecimalType::NativeType getFractionalPart(const DecimalType & decimal, size_t scale) { if (scale == 0) return 0; return getFractionalPartWithScaleMultiplier(decimal, scaleMultiplier(scale)); } /// Decimal to integer/float conversion template To convertTo(const DecimalType & decimal, size_t scale) { using NativeT = typename DecimalType::NativeType; if constexpr (std::is_floating_point_v) { return static_cast(decimal.value) / static_cast(scaleMultiplier(scale)); } else if constexpr (is_integer_v && (sizeof(To) >= sizeof(NativeT))) { NativeT whole = getWholePart(decimal, scale); if constexpr (is_unsigned_v) if (whole < 0) throw Exception("Convert overflow", ErrorCodes::DECIMAL_OVERFLOW); return static_cast(whole); } else if constexpr (is_integer_v) { using ToNativeT = typename NativeType::Type; using CastTo = std::conditional_t<(is_big_int_v && std::is_same_v), uint8_t, ToNativeT>; const NativeT whole = getWholePart(decimal, scale); static const constexpr CastTo min_to = std::numeric_limits::min(); static const constexpr CastTo max_to = std::numeric_limits::max(); if (whole < min_to || whole > max_to) throw Exception("Convert overflow", ErrorCodes::DECIMAL_OVERFLOW); return static_cast(whole); } } } }