Make behaviour of remainder of division for floating point numbers compatible with most of DBMS #7323

This commit is contained in:
Alexey Milovidov 2020-07-20 04:03:46 +03:00
parent b8cc2bee53
commit 10355a2850
6 changed files with 33 additions and 9 deletions

View File

@ -100,10 +100,12 @@ template <typename A, typename B> struct ResultOfIntegerDivision
*/ */
template <typename A, typename B> struct ResultOfModulo template <typename A, typename B> struct ResultOfModulo
{ {
using Type = typename Construct< using Type = std::conditional_t<std::is_floating_point_v<A> || std::is_floating_point_v<B>,
is_signed_v<A> || is_signed_v<B>, Float64,
false, typename Construct<
sizeof(B)>::Type; is_signed_v<A> || is_signed_v<B>,
false,
sizeof(B)>::Type>;
}; };
template <typename A> struct ResultOfNegate template <typename A> struct ResultOfNegate

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cmath>
#include <type_traits> #include <type_traits>
#include <Common/Exception.h> #include <Common/Exception.h>
#include <DataTypes/NumberTraits.h> #include <DataTypes/NumberTraits.h>
@ -86,8 +87,15 @@ struct ModuloImpl
template <typename Result = ResultType> template <typename Result = ResultType>
static inline Result apply(A a, B b) static inline Result apply(A a, B b)
{ {
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<B>::Type(b)); if constexpr (std::is_floating_point_v<ResultType>)
return typename NumberTraits::ToInteger<A>::Type(a) % typename NumberTraits::ToInteger<B>::Type(b); {
return ResultType(a) - trunc(ResultType(a) / ResultType(b)) * ResultType(b);
}
else
{
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<B>::Type(b));
return typename NumberTraits::ToInteger<A>::Type(a) % typename NumberTraits::ToInteger<B>::Type(b);
}
} }
#if USE_EMBEDDED_COMPILER #if USE_EMBEDDED_COMPILER

View File

@ -102,6 +102,7 @@ using FunctionModulo = FunctionBinaryArithmetic<ModuloImpl, NameModulo, false>;
void registerFunctionModulo(FunctionFactory & factory) void registerFunctionModulo(FunctionFactory & factory)
{ {
factory.registerFunction<FunctionModulo>(); factory.registerFunction<FunctionModulo>();
factory.registerAlias("mod", "modulo", FunctionFactory::CaseInsensitive);
} }
} }

View File

@ -14,10 +14,17 @@ struct ModuloOrZeroImpl
template <typename Result = ResultType> template <typename Result = ResultType>
static inline Result apply(A a, B b) static inline Result apply(A a, B b)
{ {
if (unlikely(divisionLeadsToFPE(a, b))) if constexpr (std::is_floating_point_v<ResultType>)
return 0; {
return ResultType(a) - floor(ResultType(a) / ResultType(b)) * ResultType(b);
}
else
{
if (unlikely(divisionLeadsToFPE(a, b)))
return 0;
return ModuloImpl<A, B>::template apply<Result>(a, b); return ModuloImpl<A, B>::template apply<Result>(a, b);
}
} }
#if USE_EMBEDDED_COMPILER #if USE_EMBEDDED_COMPILER

View File

@ -0,0 +1,3 @@
1 -1 1 -1
0.125 -0.125 0.125 -0.125
1 -1 1 -1

View File

@ -0,0 +1,3 @@
WITH 8.5 AS a, 2.5 AS b SELECT a % b, -a % b, a % -b, -a % -b;
WITH 10.125 AS a, 2.5 AS b SELECT a % b, -a % b, a % -b, -a % -b;
WITH 8.5 AS a, 2.5 AS b SELECT mod(a, b), MOD(-a, b), modulo(a, -b), moduloOrZero(-a, -b);