#pragma once #include #include namespace DB { /** Allows get the result type of the functions +, -, *, /, %, intDiv (integer division). * The rules are different from those used in C++. */ namespace NumberTraits { struct Error {}; constexpr size_t max(size_t x, size_t y) { return x > y ? x : y; } constexpr size_t min(size_t x, size_t y) { return x < y ? x : y; } /// @note There's no auto scale to larger big integer, only for integral ones. /// It's cause of (U)Int64 backward compatibility and very big performance penalties. constexpr size_t nextSize(size_t size) { if (size < 8) return size * 2; return size; } template struct Construct { using Type = Error; }; template <> struct Construct { using Type = UInt8; }; template <> struct Construct { using Type = UInt16; }; template <> struct Construct { using Type = UInt32; }; template <> struct Construct { using Type = UInt64; }; template <> struct Construct { using Type = UInt128; }; template <> struct Construct { using Type = UInt256; }; template <> struct Construct { using Type = Float32; }; template <> struct Construct { using Type = Float32; }; template <> struct Construct { using Type = Float32; }; template <> struct Construct { using Type = Float64; }; template <> struct Construct { using Type = Int8; }; template <> struct Construct { using Type = Int16; }; template <> struct Construct { using Type = Int32; }; template <> struct Construct { using Type = Int64; }; template <> struct Construct { using Type = Int128; }; template <> struct Construct { using Type = Int256; }; template <> struct Construct { using Type = Float32; }; template <> struct Construct { using Type = Float32; }; template <> struct Construct { using Type = Float32; }; template <> struct Construct { using Type = Float64; }; /** The result of addition or multiplication is calculated according to the following rules: * - if one of the arguments is floating-point, the result is a floating point, otherwise - the whole; * - if one of the arguments is signed, the result is signed, otherwise it is unsigned; * - the result contains more bits (not only meaningful) than the maximum in the arguments * (for example, UInt8 + Int32 = Int64). */ template struct ResultOfAdditionMultiplication { using Type = typename Construct< is_signed_v || is_signed_v, std::is_floating_point_v || std::is_floating_point_v, nextSize(max(sizeof(A), sizeof(B)))>::Type; }; template struct ResultOfSubtraction { using Type = typename Construct< true, std::is_floating_point_v || std::is_floating_point_v, nextSize(max(sizeof(A), sizeof(B)))>::Type; }; /** When dividing, you always get a floating-point number. */ template struct ResultOfFloatingPointDivision { using Type = Float64; }; /** For integer division, we get a number with the same number of bits as in divisible. */ template struct ResultOfIntegerDivision { using Type = typename Construct< is_signed_v || is_signed_v, false, sizeof(A)>::Type; }; /** Division with remainder you get a number with the same number of bits as in divisor, * or larger in case of signed type. */ template struct ResultOfModulo { static constexpr bool result_is_signed = is_signed_v; /// If modulo of division can yield negative number, we need larger type to accommodate it. /// Example: toInt32(-199) % toUInt8(200) will return -199 that does not fit in Int8, only in Int16. static constexpr size_t size_of_result = result_is_signed ? nextSize(sizeof(B)) : sizeof(B); using Type0 = typename Construct::Type; using Type = std::conditional_t || std::is_floating_point_v, Float64, Type0>; }; template struct ResultOfPositiveModulo { /// function positive_modulo always return non-negative number. static constexpr size_t size_of_result = sizeof(B); using Type0 = typename Construct::Type; using Type = std::conditional_t || std::is_floating_point_v, Float64, Type0>; }; template struct ResultOfModuloLegacy { using Type0 = typename Construct || is_signed_v, false, sizeof(B)>::Type; using Type = std::conditional_t || std::is_floating_point_v, Float64, Type0>; }; template struct ResultOfNegate { using Type = typename Construct< true, std::is_floating_point_v, is_signed_v ? sizeof(A) : nextSize(sizeof(A))>::Type; }; template struct ResultOfAbs { using Type = typename Construct< false, std::is_floating_point_v, sizeof(A)>::Type; }; /** For bitwise operations, an integer is obtained with number of bits is equal to the maximum of the arguments. */ template struct ResultOfBit { using Type = typename Construct< is_signed_v || is_signed_v, false, std::is_floating_point_v || std::is_floating_point_v ? 8 : max(sizeof(A), sizeof(B))>::Type; }; template struct ResultOfBitNot { using Type = typename Construct< is_signed_v, false, sizeof(A)>::Type; }; /** Type casting for `if` function: * UInt, UInt -> UInt * Int, Int -> Int * Float, Float -> Float * UInt, Int -> Int * Float, [U]Int -> Float * Decimal, Decimal -> Decimal * UUID, UUID -> UUID * UInt64, Int -> Error * Float, [U]Int64 -> Error */ template struct ResultOfIf { static constexpr bool has_float = std::is_floating_point_v || std::is_floating_point_v; static constexpr bool has_integer = is_integer || is_integer; static constexpr bool has_signed = is_signed_v || is_signed_v; static constexpr bool has_unsigned = !is_signed_v || !is_signed_v; static constexpr bool has_big_int = is_big_int_v || is_big_int_v; static constexpr size_t max_size_of_unsigned_integer = max(is_signed_v ? 0 : sizeof(A), is_signed_v ? 0 : sizeof(B)); static constexpr size_t max_size_of_signed_integer = max(is_signed_v ? sizeof(A) : 0, is_signed_v ? sizeof(B) : 0); static constexpr size_t max_size_of_integer = max(is_integer ? sizeof(A) : 0, is_integer ? sizeof(B) : 0); static constexpr size_t max_size_of_float = max(std::is_floating_point_v ? sizeof(A) : 0, std::is_floating_point_v ? sizeof(B) : 0); using ConstructedType = typename Construct= max_size_of_float) || (has_signed && has_unsigned && max_size_of_unsigned_integer >= max_size_of_signed_integer)) ? max(sizeof(A), sizeof(B)) * 2 : max(sizeof(A), sizeof(B))>::Type; using Type = std::conditional_t, A, std::conditional_t && is_decimal, std::conditional_t<(sizeof(A) > sizeof(B)), A, B>, std::conditional_t && !is_decimal, ConstructedType, Error>>>; }; /** Before applying operator `%` and bitwise operations, operands are casted to whole numbers. */ template struct ToInteger { using Type = typename Construct< is_signed_v, false, std::is_floating_point_v ? 8 : sizeof(A)>::Type; }; // CLICKHOUSE-29. The same depth, different signs // NOTE: This case is applied for 64-bit integers only (for backward compatibility), but could be used for any-bit integers /// NOLINTBEGIN(misc-redundant-expression) template constexpr bool LeastGreatestSpecialCase = std::is_integral_v && std::is_integral_v && (8 == sizeof(A) && sizeof(A) == sizeof(B)) && (is_signed_v ^ is_signed_v); /// NOLINTEND(misc-redundant-expression) template using ResultOfLeast = std::conditional_t, typename Construct::Type, typename ResultOfIf::Type>; template using ResultOfGreatest = std::conditional_t, typename Construct::Type, typename ResultOfIf::Type>; } template static inline auto littleBits(const T & x) { return static_cast(x); } }