Fix assert in "lcm"

This commit is contained in:
Alexey Milovidov 2020-08-08 02:01:05 +03:00
parent 8f16c542b6
commit ec281cd703
3 changed files with 55 additions and 3 deletions

View File

@ -1,6 +1,28 @@
#include <Functions/FunctionFactory.h>
#include <Functions/FunctionBinaryArithmetic.h>
#include <numeric>
#include <limits>
#include <type_traits>
namespace
{
template <typename T>
constexpr T abs(T value) noexcept
{
if constexpr (std::is_signed_v<T>)
{
if (value >= 0 || value == std::numeric_limits<T>::min())
return value;
return -value;
}
else
return value;
}
}
namespace DB
@ -18,9 +40,21 @@ struct LCMImpl
{
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<B>::Type(b));
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<B>::Type(b), typename NumberTraits::ToInteger<A>::Type(a));
return std::lcm(
typename NumberTraits::ToInteger<Result>::Type(a),
typename NumberTraits::ToInteger<Result>::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<Result>::Type;
Int val1 = abs<Int>(a) / std::gcd(Int(a), Int(b));
Int val2 = abs<Int>(b);
/// Overflow in implementation specific way.
return Result(val1 * val2);
}
#if USE_EMBEDDED_COMPILER

View File

@ -0,0 +1,8 @@
30
30
30
30
18446744073709551360
-256
-256
-256

View File

@ -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 lcm(256, 9223372036854775807);
SELECT lcm(256, -9223372036854775807);
SELECT lcm(-256, 9223372036854775807);
SELECT lcm(-256, -9223372036854775807);