1#pragma once
2
3#include <type_traits>
4
5#include <Core/Types.h>
6#include <Common/UInt128.h>
7
8
9namespace DB
10{
11
12/** Allows get the result type of the functions +, -, *, /, %, intDiv (integer division).
13 * The rules are different from those used in C++.
14 */
15
16namespace NumberTraits
17{
18
19struct Error {};
20
21constexpr size_t max(size_t x, size_t y)
22{
23 return x > y ? x : y;
24}
25
26constexpr size_t min(size_t x, size_t y)
27{
28 return x < y ? x : y;
29}
30
31constexpr size_t nextSize(size_t size)
32{
33 return min(size * 2, 8);
34}
35
36template <bool is_signed, bool is_floating, size_t size>
37struct Construct
38{
39 using Type = Error;
40};
41
42template <> struct Construct<false, false, 1> { using Type = UInt8; };
43template <> struct Construct<false, false, 2> { using Type = UInt16; };
44template <> struct Construct<false, false, 4> { using Type = UInt32; };
45template <> struct Construct<false, false, 8> { using Type = UInt64; };
46template <> struct Construct<false, true, 1> { using Type = Float32; };
47template <> struct Construct<false, true, 2> { using Type = Float32; };
48template <> struct Construct<false, true, 4> { using Type = Float32; };
49template <> struct Construct<false, true, 8> { using Type = Float64; };
50template <> struct Construct<true, false, 1> { using Type = Int8; };
51template <> struct Construct<true, false, 2> { using Type = Int16; };
52template <> struct Construct<true, false, 4> { using Type = Int32; };
53template <> struct Construct<true, false, 8> { using Type = Int64; };
54template <> struct Construct<true, true, 1> { using Type = Float32; };
55template <> struct Construct<true, true, 2> { using Type = Float32; };
56template <> struct Construct<true, true, 4> { using Type = Float32; };
57template <> struct Construct<true, true, 8> { using Type = Float64; };
58
59
60/** The result of addition or multiplication is calculated according to the following rules:
61 * - if one of the arguments is floating-point, the result is a floating point, otherwise - the whole;
62 * - if one of the arguments is signed, the result is signed, otherwise it is unsigned;
63 * - the result contains more bits (not only meaningful) than the maximum in the arguments
64 * (for example, UInt8 + Int32 = Int64).
65 */
66template <typename A, typename B> struct ResultOfAdditionMultiplication
67{
68 using Type = typename Construct<
69 is_signed_v<A> || is_signed_v<B>,
70 std::is_floating_point_v<A> || std::is_floating_point_v<B>,
71 nextSize(max(sizeof(A), sizeof(B)))>::Type;
72};
73
74template <typename A, typename B> struct ResultOfSubtraction
75{
76 using Type = typename Construct<
77 true,
78 std::is_floating_point_v<A> || std::is_floating_point_v<B>,
79 nextSize(max(sizeof(A), sizeof(B)))>::Type;
80};
81
82/** When dividing, you always get a floating-point number.
83 */
84template <typename A, typename B> struct ResultOfFloatingPointDivision
85{
86 using Type = Float64;
87};
88
89/** For integer division, we get a number with the same number of bits as in divisible.
90 */
91template <typename A, typename B> struct ResultOfIntegerDivision
92{
93 using Type = typename Construct<
94 is_signed_v<A> || is_signed_v<B>,
95 false,
96 sizeof(A)>::Type;
97};
98
99/** Division with remainder you get a number with the same number of bits as in divisor.
100 */
101template <typename A, typename B> struct ResultOfModulo
102{
103 using Type = typename Construct<
104 is_signed_v<A> || is_signed_v<B>,
105 false,
106 sizeof(B)>::Type;
107};
108
109template <typename A> struct ResultOfNegate
110{
111 using Type = typename Construct<
112 true,
113 std::is_floating_point_v<A>,
114 is_signed_v<A> ? sizeof(A) : nextSize(sizeof(A))>::Type;
115};
116
117template <typename A> struct ResultOfAbs
118{
119 using Type = typename Construct<
120 false,
121 std::is_floating_point_v<A>,
122 sizeof(A)>::Type;
123};
124
125/** For bitwise operations, an integer is obtained with number of bits is equal to the maximum of the arguments.
126 */
127template <typename A, typename B> struct ResultOfBit
128{
129 using Type = typename Construct<
130 is_signed_v<A> || is_signed_v<B>,
131 false,
132 std::is_floating_point_v<A> || std::is_floating_point_v<B> ? 8 : max(sizeof(A), sizeof(B))>::Type;
133};
134
135template <typename A> struct ResultOfBitNot
136{
137 using Type = typename Construct<
138 is_signed_v<A>,
139 false,
140 sizeof(A)>::Type;
141};
142
143
144/** Type casting for `if` function:
145 * UInt<x>, UInt<y> -> UInt<max(x,y)>
146 * Int<x>, Int<y> -> Int<max(x,y)>
147 * Float<x>, Float<y> -> Float<max(x, y)>
148 * UInt<x>, Int<y> -> Int<max(x*2, y)>
149 * Float<x>, [U]Int<y> -> Float<max(x, y*2)>
150 * Decimal<x>, Decimal<y> -> Decimal<max(x,y)>
151 * UUID, UUID -> UUID
152 * UInt64 , Int<x> -> Error
153 * Float<x>, [U]Int64 -> Error
154 */
155template <typename A, typename B>
156struct ResultOfIf
157{
158 static constexpr bool has_float = std::is_floating_point_v<A> || std::is_floating_point_v<B>;
159 static constexpr bool has_integer = is_integral_v<A> || is_integral_v<B>;
160 static constexpr bool has_signed = is_signed_v<A> || is_signed_v<B>;
161 static constexpr bool has_unsigned = !is_signed_v<A> || !is_signed_v<B>;
162
163 static constexpr size_t max_size_of_unsigned_integer = max(is_signed_v<A> ? 0 : sizeof(A), is_signed_v<B> ? 0 : sizeof(B));
164 static constexpr size_t max_size_of_signed_integer = max(is_signed_v<A> ? sizeof(A) : 0, is_signed_v<B> ? sizeof(B) : 0);
165 static constexpr size_t max_size_of_integer = max(is_integral_v<A> ? sizeof(A) : 0, is_integral_v<B> ? sizeof(B) : 0);
166 static constexpr size_t max_size_of_float = max(std::is_floating_point_v<A> ? sizeof(A) : 0, std::is_floating_point_v<B> ? sizeof(B) : 0);
167
168 using ConstructedType = typename Construct<has_signed, has_float,
169 ((has_float && has_integer && max_size_of_integer >= max_size_of_float)
170 || (has_signed && has_unsigned && max_size_of_unsigned_integer >= max_size_of_signed_integer))
171 ? max(sizeof(A), sizeof(B)) * 2
172 : max(sizeof(A), sizeof(B))>::Type;
173
174 using ConstructedWithUUID = std::conditional_t<std::is_same_v<A, UInt128> && std::is_same_v<B, UInt128>, A, ConstructedType>;
175
176 using Type = std::conditional_t<!IsDecimalNumber<A> && !IsDecimalNumber<B>, ConstructedWithUUID,
177 std::conditional_t<IsDecimalNumber<A> && IsDecimalNumber<B>, std::conditional_t<(sizeof(A) > sizeof(B)), A, B>, Error>>;
178};
179
180/** Before applying operator `%` and bitwise operations, operands are casted to whole numbers. */
181template <typename A> struct ToInteger
182{
183 using Type = typename Construct<
184 is_signed_v<A>,
185 false,
186 std::is_floating_point_v<A> ? 8 : sizeof(A)>::Type;
187};
188
189
190// CLICKHOUSE-29. The same depth, different signs
191// NOTE: This case is applied for 64-bit integers only (for backward compatibility), but could be used for any-bit integers
192template <typename A, typename B>
193constexpr bool LeastGreatestSpecialCase =
194 is_integral_v<A> && is_integral_v<B>
195 && (8 == sizeof(A) && sizeof(A) == sizeof(B))
196 && (is_signed_v<A> ^ is_signed_v<B>);
197
198template <typename A, typename B>
199using ResultOfLeast = std::conditional_t<LeastGreatestSpecialCase<A, B>,
200 typename Construct<true, false, sizeof(A)>::Type,
201 typename ResultOfIf<A, B>::Type>;
202
203template <typename A, typename B>
204using ResultOfGreatest = std::conditional_t<LeastGreatestSpecialCase<A, B>,
205 typename Construct<false, false, sizeof(A)>::Type,
206 typename ResultOfIf<A, B>::Type>;
207
208}
209
210}
211