// Copyright 2023 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include namespace DB { template struct DivideFloatingImpl { using ResultType = typename NumberTraits::ResultOfFloatingPointDivision::Type; template static Result apply(A a, B b) { return static_cast(a) / b; } template static Result apply(A, B, UInt8 &) { throw Exception("Should not reach here"); } }; template struct DivideFloatingImpl { using ResultPrecInferer = DivDecimalInferer; using ResultType = If || std::is_floating_point_v, double, Decimal32>; template static Result apply(A a, B b) { return static_cast(a) / static_cast(b); } template static Result apply(A, B, UInt8 &) { throw Exception("Should not reach here"); } }; template struct TiDBDivideFloatingImpl { using ResultType = typename NumberTraits::ResultOfFloatingPointDivision::Type; template static Result apply(A x, B d) { /// ref https://github.com/pingcap/tiflash/issues/6462 /// For division of Decimal/Decimal or Int/Decimal or Decimal/Int, we should round the result to make compatible with TiDB. /// basically refer to https://stackoverflow.com/a/71634489 if constexpr (std::is_integral_v || std::is_same_v || std::is_same_v) { /// 1. do division first, get the quotient and mod, todo:(perf) find a unified `divmod` function to speed up this. Result quotient = x / d; Result mod = x % d; /// 2. get the half of divisor, which is threshold to decide whether to round up or down. /// note: don't directly use bit operation here, it may cause unexpected result. Result half = (d / 2) + (d % 2); /// 3. compare the abstract values of mod and half, if mod >= half, then round up. Result abs_m = mod < 0 ? -mod : mod; Result abs_h = half < 0 ? -half : half; if (abs_m >= abs_h) { /// 4. now we need to round up, i.e., add 1 to the quotient's absolute value. /// if the signs of dividend and divisor are the same, then the quotient should be positive, otherwise negative. if ((x < 0) == (d < 0)) // same_sign, i.e., quotient >= 0 quotient = quotient + 1; else quotient = quotient - 1; } return quotient; } else return static_cast(x) / d; } template static Result apply(A a, B b, UInt8 & res_null) { if (b == 0) { /// we can check res_null to see if it is DIVISION_BY_ZERO or DIVISION_BY_NULL, when sql mode is ERROR_FOR_DIVISION_BY_ZERO, /// inserts and updates involving expressions that perform division by zero should be treated as errors, now only read-only /// statement will send to TiFlash, so just return NULL here res_null = 1; return static_cast(0); } return apply(a, b); } }; template struct TiDBDivideFloatingImpl { using ResultPrecInferer = DivDecimalInferer; using ResultType = If || std::is_floating_point_v, double, Decimal32>; template static Result apply(A a, B b) { return static_cast(a) / static_cast(b); } template static Result apply(A a, B b, UInt8 & res_null) { if (static_cast(b) == static_cast(0)) { /// we can check res_null to see if it is DIVISION_BY_ZERO or DIVISION_BY_NULL, when sql mode is ERROR_FOR_DIVISION_BY_ZERO, /// inserts and updates involving expressions that perform division by zero should be treated as errors, now only read-only /// statement will send to TiFlash, so just return NULL here res_null = 1; return static_cast(0); } return apply(a, b); } }; template struct DivideIntegralImpl { using ResultType = typename NumberTraits::ResultOfIntegerDivision::Type; template static Result apply(A a, B b) { throwIfDivisionLeadsToFPE(a, b); return static_cast(a) / static_cast(b); } template static Result apply(A, B, UInt8 &) { throw Exception("Should not reach here"); } }; template struct DivideIntegralImpl { using ResultType = If || std::is_unsigned_v, uint64_t, int64_t>; template static Result apply(A a, B b) { Result x, y; if constexpr (IsDecimal) { x = static_cast(a.value); } else { x = static_cast(a); } if constexpr (IsDecimal) { y = static_cast(b.value); } else { y = static_cast(b); } throwIfDivisionLeadsToFPE(x, y); return x / y; } template static Result apply(A, B, UInt8 &) { throw Exception("Should not reach here"); } }; template struct DivideIntegralOrZeroImpl { using ResultType = typename NumberTraits::ResultOfIntegerDivision::Type; template static Result apply(A a, B b) { return static_cast( unlikely(divisionLeadsToFPE(a, b)) ? 0 : static_cast(a) / static_cast(b)); } template static Result apply(A, B, UInt8 &) { throw Exception("Should not reach here"); } }; template struct DivideIntegralOrZeroImpl { using ResultType = If || std::is_unsigned_v, uint64_t, int64_t>; template static Result apply(A a, B b) { Result x, y; if constexpr (IsDecimal) { x = static_cast(a.value); } else { x = static_cast(a); } if constexpr (IsDecimal) { y = static_cast(b.value); } else { y = static_cast(b); } throwIfDivisionLeadsToFPE(x, y); return unlikely(divisionLeadsToFPE(x, y)) ? 0 : x / y; } template static Result apply(A, B, UInt8 &) { throw Exception("Should not reach here"); } }; /// Optimizations for integer division by a constant. #if __SSE2__ #define LIBDIVIDE_SSE2 1 #endif #include template struct DivideIntegralByConstantImpl : BinaryOperationImplBase> { using ResultType = typename DivideIntegralImpl::ResultType; static void vectorConstant(const PaddedPODArray & a, B b, PaddedPODArray & c) { if (unlikely(b == 0)) throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION); if constexpr (is_signed_v) { if (unlikely(b == -1)) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) c[i] = -c[i]; return; } } libdivide::divider divider(b); size_t size = a.size(); const A * a_pos = &a[0]; const A * a_end = a_pos + size; ResultType * c_pos = &c[0]; #if __SSE2__ static constexpr size_t values_per_sse_register = 16 / sizeof(A); const A * a_end_sse = a_pos + size / values_per_sse_register * values_per_sse_register; while (a_pos < a_end_sse) { _mm_storeu_si128( reinterpret_cast<__m128i *>(c_pos), _mm_loadu_si128(reinterpret_cast(a_pos)) / divider); a_pos += values_per_sse_register; c_pos += values_per_sse_register; } #endif while (a_pos < a_end) { *c_pos = *a_pos / divider; ++a_pos; ++c_pos; } } }; /** Specializations are specified for dividing numbers of the type UInt64 and UInt32 by the numbers of the same sign. * Can be expanded to all possible combinations, but more code is needed. */ // clang-format off template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; template <> struct BinaryOperationImpl> : DivideIntegralByConstantImpl {}; // clang-format on namespace { // clang-format off struct NameDivideFloating { static constexpr auto name = "divide"; }; struct NameTiDBDivideFloating { static constexpr auto name = "tidbDivide"; }; struct NameDivideIntegral { static constexpr auto name = "intDiv"; }; struct NameDivideIntegralOrZero { static constexpr auto name = "intDivOrZero"; }; // clang-format on using FunctionDivideFloating = FunctionBinaryArithmetic; using FunctionTiDBDivideFloating = FunctionBinaryArithmetic; using FunctionDivideIntegral = FunctionBinaryArithmetic; using FunctionDivideIntegralOrZero = FunctionBinaryArithmetic; } // namespace void registerFunctionDivideFloating(FunctionFactory & factory) { factory.registerFunction(); } void registerFunctionTiDBDivideFloating(FunctionFactory & factory) { factory.registerFunction(); } void registerFunctionDivideIntegral(FunctionFactory & factory) { factory.registerFunction(); } void registerFunctionDivideIntegralOrZero(FunctionFactory & factory) { factory.registerFunction(); } } // namespace DB