// 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 ModuloImpl { using ResultType = typename NumberTraits::ResultOfModulo::Type; template static Result apply(A a, B b) { if constexpr (std::is_floating_point_v) { auto x = static_cast(a); auto y = static_cast(b); // assert no infinite or NaN values. assert(std::isfinite(x) && std::isfinite(y)); // C++ does not allow operator% between floating point // values, so we call into std::fmod. return std::fmod(x, y); } else // both A and B are integrals. { // decimals are expected to be converted to integers or floating point values before computations. static_assert(is_integer_v); // convert to unsigned before computing. // we have to prevent wrong result like UInt64(5) = UInt64(5) % Int64(-3). // in MySQL, UInt64(5) % Int64(-3) evaluates to UInt64(2). auto x = toSafeUnsigned(a); auto y = toSafeUnsigned(b); auto result = static_cast(x % y); // in MySQL, the sign of a % b is the same as that of a. // e.g. 5 % -3 = 2, -5 % 3 = -2. if constexpr (is_signed_v) { if (a < 0) return -result; else return result; } else return result; } } template static Result apply(A a, B b, UInt8 & res_null) { if (unlikely(b == 0)) { res_null = 1; return static_cast(0); } return apply(a, b); } }; template struct ModuloImpl { using ResultPrecInferer = ModDecimalInferer; using ResultType = If || std::is_floating_point_v, double, Decimal32>; 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); return ModuloImpl::apply(x, y); } template static Result apply(A a, B b, UInt8 & res_null) { if (unlikely(b == 0)) { res_null = 1; return static_cast(0); } return apply(a, b); } }; /// Optimizations for integer division by a constant. #if __SSE2__ #define LIBDIVIDE_SSE2 1 #endif #include template struct ModuloByConstantImpl : BinaryOperationImplBase> { using ResultType = typename ModuloImpl::ResultType; static void vectorConstant(const PaddedPODArray & a, B b, PaddedPODArray & c) { if (unlikely(b == 0)) throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" if (unlikely((std::is_signed_v && b == -1) || b == 1)) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) c[i] = 0; return; } #pragma GCC diagnostic pop libdivide::divider divider(b); /// Here we failed to make the SSE variant from libdivide give an advantage. size_t size = a.size(); for (size_t i = 0; i < size; ++i) c[i] = a[i] - (a[i] / divider) * b; /// NOTE: perhaps, the division semantics with the remainder of negative numbers is not preserved. } }; /** 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> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; template <> struct BinaryOperationImpl> : ModuloByConstantImpl {}; // clang-format on namespace { // clang-format off struct NameModulo { static constexpr auto name = "modulo"; }; // clang-format on using FunctionModulo = FunctionBinaryArithmetic; } // namespace void registerFunctionModulo(FunctionFactory & factory) { factory.registerFunction(); } } // namespace DB