#pragma once #include #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; } constexpr size_t nextSize(size_t size) { return min(size * 2, 8); } 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 = 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 = 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. */ template struct ResultOfModulo { using Type = typename Construct< is_signed_v || is_signed_v, false, sizeof(B)>::Type; }; 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_integral_v || is_integral_v; static constexpr bool has_signed = is_signed_v || is_signed_v; static constexpr bool has_unsigned = !is_signed_v || !is_signed_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_integral_v ? sizeof(A) : 0, is_integral_v ? 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 ConstructedWithUUID = std::conditional_t && std::is_same_v, A, ConstructedType>; using Type = std::conditional_t && !IsDecimalNumber, ConstructedWithUUID, std::conditional_t && IsDecimalNumber, std::conditional_t<(sizeof(A) > sizeof(B)), A, B>, 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 template constexpr bool LeastGreatestSpecialCase = is_integral_v && is_integral_v && (8 == sizeof(A) && sizeof(A) == sizeof(B)) && (is_signed_v ^ is_signed_v); template using ResultOfLeast = std::conditional_t, typename Construct::Type, typename ResultOfIf::Type>; template using ResultOfGreatest = std::conditional_t, typename Construct::Type, typename ResultOfIf::Type>; } }