mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-24 16:42:05 +00:00
Fixed overflow in intDiv functions
This commit is contained in:
parent
8949ef6dd3
commit
52650ce2f7
@ -13,6 +13,7 @@ namespace DB
|
|||||||
namespace ErrorCodes
|
namespace ErrorCodes
|
||||||
{
|
{
|
||||||
extern const int ILLEGAL_DIVISION;
|
extern const int ILLEGAL_DIVISION;
|
||||||
|
extern const int LOGICAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
@ -55,7 +56,18 @@ struct DivideIntegralImpl
|
|||||||
static inline Result apply(A a, B b)
|
static inline Result apply(A a, B b)
|
||||||
{
|
{
|
||||||
throwIfDivisionLeadsToFPE(a, b);
|
throwIfDivisionLeadsToFPE(a, b);
|
||||||
return a / b;
|
|
||||||
|
if constexpr (!std::is_same_v<ResultType, NumberTraits::Error>)
|
||||||
|
{
|
||||||
|
/// Otherwise overflow may occur due to integer promotion. Example: int8_t(-1) / uint64_t(2).
|
||||||
|
/// NOTE: overflow is still possible when dividing large signed number to large unsigned number or vice-versa. But it's less harmful.
|
||||||
|
if constexpr (std::is_integral_v<A> && std::is_integral_v<B> && (std::is_signed_v<A> || std::is_signed_v<B>))
|
||||||
|
return std::make_signed_t<A>(a) / std::make_signed_t<B>(b);
|
||||||
|
else
|
||||||
|
return a / b;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw Exception("Logical error: the types are not divisable", ErrorCodes::LOGICAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if USE_EMBEDDED_COMPILER
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
#include <Functions/FunctionFactory.h>
|
#include <Functions/FunctionFactory.h>
|
||||||
#include <Functions/FunctionBinaryArithmetic.h>
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
#include "intDiv.h"
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace ErrorCodes
|
||||||
|
{
|
||||||
|
extern const int LOGICAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename A, typename B>
|
template <typename A, typename B>
|
||||||
struct DivideIntegralOrZeroImpl
|
struct DivideIntegralOrZeroImpl
|
||||||
{
|
{
|
||||||
@ -12,7 +20,13 @@ struct DivideIntegralOrZeroImpl
|
|||||||
template <typename Result = ResultType>
|
template <typename Result = ResultType>
|
||||||
static inline Result apply(A a, B b)
|
static inline Result apply(A a, B b)
|
||||||
{
|
{
|
||||||
return unlikely(divisionLeadsToFPE(a, b)) ? 0 : a / b;
|
if (unlikely(divisionLeadsToFPE(a, b)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if constexpr (!std::is_same_v<ResultType, NumberTraits::Error>)
|
||||||
|
return DivideIntegralImpl<A, B>::apply(a, b);
|
||||||
|
else
|
||||||
|
throw Exception("Logical error: the types are not divisable", ErrorCodes::LOGICAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if USE_EMBEDDED_COMPILER
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
23
dbms/tests/queries/0_stateless/00977_int_div.reference
Normal file
23
dbms/tests/queries/0_stateless/00977_int_div.reference
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
-2000 -1 1
|
||||||
|
-1
|
||||||
|
-1
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
19
dbms/tests/queries/0_stateless/00977_int_div.sql
Normal file
19
dbms/tests/queries/0_stateless/00977_int_div.sql
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
SELECT
|
||||||
|
sum(ASD) AS asd,
|
||||||
|
intDiv(toInt64(asd), abs(toInt64(asd))) AS int_div_with_abs,
|
||||||
|
intDiv(toInt64(asd), toInt64(asd)) AS int_div_without_abs
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT ASD
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT [-1000, -1000] AS asds
|
||||||
|
)
|
||||||
|
ARRAY JOIN asds AS ASD
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT intDivOrZero( CAST(-1000, 'Int64') , CAST(1000, 'UInt64') );
|
||||||
|
SELECT intDivOrZero( CAST(-1000, 'Int64') , CAST(1000, 'Int64') );
|
||||||
|
|
||||||
|
SELECT intDiv(-1, number) FROM numbers(1, 10);
|
||||||
|
SELECT intDivOrZero(-1, number) FROM numbers(1, 10);
|
Loading…
Reference in New Issue
Block a user