#pragma once #include #include #include #include #if !defined(ARCADIA_BUILD) # include #endif 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); return a / b; } #pragma GCC diagnostic pop template struct DivideIntegralImpl { using ResultType = typename NumberTraits::ResultOfIntegerDivision::Type; static const constexpr bool allow_fixed_string = false; template static inline Result apply(A a, B 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_integral_v && is_integral_v && (is_signed_v || is_signed_v)) return checkedDivision(std::make_signed_t(a), sizeof(A) > sizeof(B) ? std::make_signed_t(b) : std::make_signed_t(b)); else return checkedDivision(a, b); } #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; static const constexpr bool allow_fixed_string = 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 ResultType(a) - trunc(ResultType(a) / ResultType(b)) * ResultType(b); } else { throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger::Type(a), typename NumberTraits::ToInteger::Type(b)); return typename NumberTraits::ToInteger::Type(a) % typename NumberTraits::ToInteger::Type(b); } } #if USE_EMBEDDED_COMPILER static constexpr bool compilable = false; /// don't know how to throw from LLVM IR #endif }; }