1 | #pragma once |
2 | |
3 | #include <type_traits> |
4 | |
5 | #include <Core/Types.h> |
6 | #include <Common/UInt128.h> |
7 | |
8 | |
9 | namespace 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 | |
16 | namespace NumberTraits |
17 | { |
18 | |
19 | struct Error {}; |
20 | |
21 | constexpr size_t max(size_t x, size_t y) |
22 | { |
23 | return x > y ? x : y; |
24 | } |
25 | |
26 | constexpr size_t min(size_t x, size_t y) |
27 | { |
28 | return x < y ? x : y; |
29 | } |
30 | |
31 | constexpr size_t nextSize(size_t size) |
32 | { |
33 | return min(size * 2, 8); |
34 | } |
35 | |
36 | template <bool is_signed, bool is_floating, size_t size> |
37 | struct Construct |
38 | { |
39 | using Type = Error; |
40 | }; |
41 | |
42 | template <> struct Construct<false, false, 1> { using Type = UInt8; }; |
43 | template <> struct Construct<false, false, 2> { using Type = UInt16; }; |
44 | template <> struct Construct<false, false, 4> { using Type = UInt32; }; |
45 | template <> struct Construct<false, false, 8> { using Type = UInt64; }; |
46 | template <> struct Construct<false, true, 1> { using Type = Float32; }; |
47 | template <> struct Construct<false, true, 2> { using Type = Float32; }; |
48 | template <> struct Construct<false, true, 4> { using Type = Float32; }; |
49 | template <> struct Construct<false, true, 8> { using Type = Float64; }; |
50 | template <> struct Construct<true, false, 1> { using Type = Int8; }; |
51 | template <> struct Construct<true, false, 2> { using Type = Int16; }; |
52 | template <> struct Construct<true, false, 4> { using Type = Int32; }; |
53 | template <> struct Construct<true, false, 8> { using Type = Int64; }; |
54 | template <> struct Construct<true, true, 1> { using Type = Float32; }; |
55 | template <> struct Construct<true, true, 2> { using Type = Float32; }; |
56 | template <> struct Construct<true, true, 4> { using Type = Float32; }; |
57 | template <> 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 | */ |
66 | template <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 | |
74 | template <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 | */ |
84 | template <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 | */ |
91 | template <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 | */ |
101 | template <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 | |
109 | template <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 | |
117 | template <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 | */ |
127 | template <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 | |
135 | template <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 | */ |
155 | template <typename A, typename B> |
156 | struct 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. */ |
181 | template <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 |
192 | template <typename A, typename B> |
193 | constexpr 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 | |
198 | template <typename A, typename B> |
199 | using ResultOfLeast = std::conditional_t<LeastGreatestSpecialCase<A, B>, |
200 | typename Construct<true, false, sizeof(A)>::Type, |
201 | typename ResultOfIf<A, B>::Type>; |
202 | |
203 | template <typename A, typename B> |
204 | using 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 | |