mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-26 09:32:01 +00:00
Merge pull request #13510 from ClickHouse/fix-assert-lcm
Fix assert in "lcm"
This commit is contained in:
commit
3af1eba808
@ -1,6 +1,28 @@
|
|||||||
#include <Functions/FunctionFactory.h>
|
#include <Functions/FunctionFactory.h>
|
||||||
#include <Functions/FunctionBinaryArithmetic.h>
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
#include <numeric>
|
#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
|
namespace DB
|
||||||
@ -18,9 +40,22 @@ struct LCMImpl
|
|||||||
{
|
{
|
||||||
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<B>::Type(b));
|
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));
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<B>::Type(b), typename NumberTraits::ToInteger<A>::Type(a));
|
||||||
return std::lcm(
|
|
||||||
typename NumberTraits::ToInteger<Result>::Type(a),
|
/** It's tempting to use std::lcm function.
|
||||||
typename NumberTraits::ToInteger<Result>::Type(b));
|
* 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;
|
||||||
|
using Unsigned = std::make_unsigned_t<Int>;
|
||||||
|
|
||||||
|
Unsigned val1 = abs<Int>(a) / std::gcd(Int(a), Int(b));
|
||||||
|
Unsigned val2 = abs<Int>(b);
|
||||||
|
|
||||||
|
/// Overflow in implementation specific way.
|
||||||
|
return Result(val1 * val2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if USE_EMBEDDED_COMPILER
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
8
tests/queries/0_stateless/01435_lcm_overflow.reference
Normal file
8
tests/queries/0_stateless/01435_lcm_overflow.reference
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
30
|
||||||
|
30
|
||||||
|
30
|
||||||
|
30
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
10
tests/queries/0_stateless/01435_lcm_overflow.sql
Normal file
10
tests/queries/0_stateless/01435_lcm_overflow.sql
Normal 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 ignore(lcm(256, 9223372036854775807));
|
||||||
|
SELECT ignore(lcm(256, -9223372036854775807));
|
||||||
|
SELECT ignore(lcm(-256, 9223372036854775807));
|
||||||
|
SELECT ignore(lcm(-256, -9223372036854775807));
|
Loading…
Reference in New Issue
Block a user