#pragma once #include #include #include #include #include #include "config_core.h" #include namespace DB { namespace ErrorCodes { extern const int ILLEGAL_DIVISION; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" template inline void throwIfDivisionLeadsToFPE(A a, B b) { /// Is it better to use siglongjmp instead of checks? if (unlikely(b == 0)) throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION); /// http://avva.livejournal.com/2548306.html if (unlikely(is_signed_v && is_signed_v && a == std::numeric_limits::min() && b == -1)) throw Exception("Division of minimal signed number by minus one", ErrorCodes::ILLEGAL_DIVISION); } template inline bool divisionLeadsToFPE(A a, B b) { if (unlikely(b == 0)) return true; if (unlikely(is_signed_v && is_signed_v && a == std::numeric_limits::min() && b == -1)) return true; return false; } template inline auto checkedDivision(A a, B b) { throwIfDivisionLeadsToFPE(a, b); if constexpr (is_big_int_v && std::is_floating_point_v) return static_cast(a) / b; else if constexpr (is_big_int_v && std::is_floating_point_v) return a / static_cast(b); else if constexpr (is_big_int_v && is_big_int_v) return static_cast(a / b); else if constexpr (!is_big_int_v && is_big_int_v) return static_cast(B(a) / b); else return a / b; } #pragma GCC diagnostic pop template struct DivideIntegralImpl { using ResultType = typename NumberTraits::ResultOfIntegerDivision::Type; static const constexpr bool allow_fixed_string = false; static const constexpr bool allow_string_integer = false; template static inline Result apply(A a, B b) { using CastA = std::conditional_t && std::is_same_v, uint8_t, A>; using CastB = std::conditional_t && std::is_same_v, uint8_t, B>; /// Otherwise overflow may occur due to integer promotion. Example: int8_t(-1) / uint64_t(2). /// NOTE: overflow is still possible when dividing large signed number to large unsigned number or vice-versa. But it's less harmful. if constexpr (is_integer && is_integer && (is_signed_v || is_signed_v)) { using SignedCastA = make_signed_t; using SignedCastB = std::conditional_t, SignedCastA>; return static_cast(checkedDivision(static_cast(a), static_cast(b))); } else { /// Comparisons are not strict to avoid rounding issues when operand is implicitly casted to float. if constexpr (std::is_floating_point_v) if (isNaN(a) || a >= std::numeric_limits::max() || a <= std::numeric_limits::lowest()) throw Exception("Cannot perform integer division on infinite or too large floating point numbers", ErrorCodes::ILLEGAL_DIVISION); if constexpr (std::is_floating_point_v) if (isNaN(b) || b >= std::numeric_limits::max() || b <= std::numeric_limits::lowest()) throw Exception("Cannot perform integer division on infinite or too large floating point numbers", ErrorCodes::ILLEGAL_DIVISION); auto res = checkedDivision(CastA(a), CastB(b)); if constexpr (std::is_floating_point_v) if (isNaN(res) || res >= static_cast(std::numeric_limits::max()) || res <= std::numeric_limits::lowest()) throw Exception("Cannot perform integer division, because it will produce infinite or too large number", ErrorCodes::ILLEGAL_DIVISION); return static_cast(res); } } #if USE_EMBEDDED_COMPILER static constexpr bool compilable = false; /// don't know how to throw from LLVM IR #endif }; template struct ModuloImpl { using ResultType = typename NumberTraits::ResultOfModulo::Type; using IntegerAType = typename NumberTraits::ToInteger::Type; using IntegerBType = typename NumberTraits::ToInteger::Type; static const constexpr bool allow_fixed_string = false; static const constexpr bool allow_string_integer = false; template static inline Result apply(A a, B b) { if constexpr (std::is_floating_point_v) { /// This computation is similar to `fmod` but the latter is not inlined and has 40 times worse performance. return static_cast(a) - trunc(static_cast(a) / static_cast(b)) * static_cast(b); } else { if constexpr (std::is_floating_point_v) if (isNaN(a) || a > std::numeric_limits::max() || a < std::numeric_limits::lowest()) throw Exception("Cannot perform integer division on infinite or too large floating point numbers", ErrorCodes::ILLEGAL_DIVISION); if constexpr (std::is_floating_point_v) if (isNaN(b) || b > std::numeric_limits::max() || b < std::numeric_limits::lowest()) throw Exception("Cannot perform integer division on infinite or too large floating point numbers", ErrorCodes::ILLEGAL_DIVISION); throwIfDivisionLeadsToFPE(IntegerAType(a), IntegerBType(b)); if constexpr (is_big_int_v || is_big_int_v) { using CastA = std::conditional_t, uint8_t, IntegerAType>; using CastB = std::conditional_t, uint8_t, IntegerBType>; CastA int_a(a); CastB int_b(b); if constexpr (is_big_int_v && sizeof(IntegerAType) <= sizeof(IntegerBType)) return static_cast(static_cast(int_a) % int_b); else return static_cast(int_a % static_cast(int_b)); } else return IntegerAType(a) % IntegerBType(b); } } #if USE_EMBEDDED_COMPILER static constexpr bool compilable = false; /// don't know how to throw from LLVM IR #endif }; template struct ModuloLegacyImpl : ModuloImpl { using ResultType = typename NumberTraits::ResultOfModuloLegacy::Type; }; }