#pragma once #include #include #include #include #include #include "config.h" namespace DB { namespace ErrorCodes { extern const int ILLEGAL_DIVISION; } template inline void throwIfDivisionLeadsToFPE(A a, B b) { /// Is it better to use siglongjmp instead of checks? if (unlikely(b == 0)) throw Exception(ErrorCodes::ILLEGAL_DIVISION, "Division by zero"); /// http://avva.livejournal.com/2548306.html if (unlikely(is_signed_v && is_signed_v && a == std::numeric_limits::min() && b == -1)) throw Exception(ErrorCodes::ILLEGAL_DIVISION, "Division of minimal signed number by minus one"); } 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; } 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(ErrorCodes::ILLEGAL_DIVISION, "Cannot perform integer division on infinite or too large floating point numbers"); if constexpr (std::is_floating_point_v) if (isNaN(b) || b >= std::numeric_limits::max() || b <= std::numeric_limits::lowest()) throw Exception(ErrorCodes::ILLEGAL_DIVISION, "Cannot perform integer division on infinite or too large floating point numbers"); 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(ErrorCodes::ILLEGAL_DIVISION, "Cannot perform integer division, because it will produce infinite or too large number"); 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(ErrorCodes::ILLEGAL_DIVISION, "Cannot perform integer division on infinite or too large floating point numbers"); if constexpr (std::is_floating_point_v) if (isNaN(b) || b > std::numeric_limits::max() || b < std::numeric_limits::lowest()) throw Exception(ErrorCodes::ILLEGAL_DIVISION, "Cannot perform integer division on infinite or too large floating point numbers"); 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 static_cast(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; }; template struct PositiveModuloImpl : ModuloImpl { using OriginResultType = typename ModuloImpl::ResultType; using ResultType = typename NumberTraits::ResultOfPositiveModulo::Type; template static inline Result apply(A a, B b) { auto res = ModuloImpl::template apply(a, b); if constexpr (is_signed_v) { if (res < 0) { if constexpr (is_unsigned_v) res += static_cast(b); else { if (b == std::numeric_limits::lowest()) throw Exception(ErrorCodes::ILLEGAL_DIVISION, "Division by the most negative number"); res += b >= 0 ? static_cast(b) : static_cast(-b); } } } return static_cast(res); } }; }