1 | #pragma once |
2 | |
3 | #include <type_traits> |
4 | #include <common/likely.h> |
5 | #include <Common/Exception.h> |
6 | #include <Common/config.h> |
7 | #include <DataTypes/NumberTraits.h> |
8 | |
9 | |
10 | namespace DB |
11 | { |
12 | |
13 | namespace ErrorCodes |
14 | { |
15 | extern const int ILLEGAL_DIVISION; |
16 | } |
17 | |
18 | #pragma GCC diagnostic push |
19 | #pragma GCC diagnostic ignored "-Wsign-compare" |
20 | |
21 | template <typename A, typename B> |
22 | inline void throwIfDivisionLeadsToFPE(A a, B b) |
23 | { |
24 | /// Is it better to use siglongjmp instead of checks? |
25 | |
26 | if (unlikely(b == 0)) |
27 | throw Exception("Division by zero" , ErrorCodes::ILLEGAL_DIVISION); |
28 | |
29 | /// http://avva.livejournal.com/2548306.html |
30 | if (unlikely(is_signed_v<A> && is_signed_v<B> && a == std::numeric_limits<A>::min() && b == -1)) |
31 | throw Exception("Division of minimal signed number by minus one" , ErrorCodes::ILLEGAL_DIVISION); |
32 | } |
33 | |
34 | template <typename A, typename B> |
35 | inline bool divisionLeadsToFPE(A a, B b) |
36 | { |
37 | if (unlikely(b == 0)) |
38 | return true; |
39 | |
40 | if (unlikely(is_signed_v<A> && is_signed_v<B> && a == std::numeric_limits<A>::min() && b == -1)) |
41 | return true; |
42 | |
43 | return false; |
44 | } |
45 | |
46 | |
47 | #pragma GCC diagnostic pop |
48 | |
49 | template <typename A, typename B> |
50 | struct DivideIntegralImpl |
51 | { |
52 | using ResultType = typename NumberTraits::ResultOfIntegerDivision<A, B>::Type; |
53 | |
54 | template <typename Result = ResultType> |
55 | static inline Result apply(A a, B b) |
56 | { |
57 | throwIfDivisionLeadsToFPE(a, b); |
58 | |
59 | /// Otherwise overflow may occur due to integer promotion. Example: int8_t(-1) / uint64_t(2). |
60 | /// NOTE: overflow is still possible when dividing large signed number to large unsigned number or vice-versa. But it's less harmful. |
61 | if constexpr (is_integral_v<A> && is_integral_v<B> && (is_signed_v<A> || is_signed_v<B>)) |
62 | return std::make_signed_t<A>(a) / std::make_signed_t<B>(b); |
63 | else |
64 | return a / b; |
65 | } |
66 | |
67 | #if USE_EMBEDDED_COMPILER |
68 | static constexpr bool compilable = false; /// don't know how to throw from LLVM IR |
69 | #endif |
70 | }; |
71 | |
72 | } |
73 | |