diff --git a/src/Functions/lcm.cpp b/src/Functions/lcm.cpp index 9230a292c43..e014fa64cc3 100644 --- a/src/Functions/lcm.cpp +++ b/src/Functions/lcm.cpp @@ -1,6 +1,28 @@ #include #include + #include +#include +#include + + +namespace +{ + +template +constexpr T abs(T value) noexcept +{ + if constexpr (std::is_signed_v) + { + if (value >= 0 || value == std::numeric_limits::min()) + return value; + return -value; + } + else + return value; +} + +} namespace DB @@ -18,9 +40,22 @@ struct LCMImpl { throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger::Type(a), typename NumberTraits::ToInteger::Type(b)); throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger::Type(b), typename NumberTraits::ToInteger::Type(a)); - return std::lcm( - typename NumberTraits::ToInteger::Type(a), - typename NumberTraits::ToInteger::Type(b)); + + /** It's tempting to use std::lcm function. + * But it has undefined behaviour on overflow. + * And assert in debug build. + * We need some well defined behaviour instead + * (example: throw an exception or overflow in implementation specific way). + */ + + using Int = typename NumberTraits::ToInteger::Type; + using Unsigned = std::make_unsigned_t; + + Unsigned val1 = abs(a) / std::gcd(Int(a), Int(b)); + Unsigned val2 = abs(b); + + /// Overflow in implementation specific way. + return Result(val1 * val2); } #if USE_EMBEDDED_COMPILER diff --git a/tests/queries/0_stateless/01435_lcm_overflow.reference b/tests/queries/0_stateless/01435_lcm_overflow.reference new file mode 100644 index 00000000000..eebd14705df --- /dev/null +++ b/tests/queries/0_stateless/01435_lcm_overflow.reference @@ -0,0 +1,8 @@ +30 +30 +30 +30 +0 +0 +0 +0 diff --git a/tests/queries/0_stateless/01435_lcm_overflow.sql b/tests/queries/0_stateless/01435_lcm_overflow.sql new file mode 100644 index 00000000000..f70200eb2d8 --- /dev/null +++ b/tests/queries/0_stateless/01435_lcm_overflow.sql @@ -0,0 +1,10 @@ +SELECT lcm(15, 10); +SELECT lcm(-15, 10); +SELECT lcm(15, -10); +SELECT lcm(-15, -10); + +-- Implementation specific result on overflow: +SELECT ignore(lcm(256, 9223372036854775807)); +SELECT ignore(lcm(256, -9223372036854775807)); +SELECT ignore(lcm(-256, 9223372036854775807)); +SELECT ignore(lcm(-256, -9223372036854775807));