Fixed overflow in intDiv functions

This commit is contained in:
Alexey Milovidov 2019-07-31 04:05:33 +03:00
parent 8949ef6dd3
commit 52650ce2f7
4 changed files with 70 additions and 2 deletions

View File

@ -13,6 +13,7 @@ namespace DB
namespace ErrorCodes
{
extern const int ILLEGAL_DIVISION;
extern const int LOGICAL_ERROR;
}
#pragma GCC diagnostic push
@ -55,8 +56,19 @@ struct DivideIntegralImpl
static inline Result apply(A a, B b)
{
throwIfDivisionLeadsToFPE(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
static constexpr bool compilable = false; /// don't know how to throw from LLVM IR

View File

@ -1,9 +1,17 @@
#include <Functions/FunctionFactory.h>
#include <Functions/FunctionBinaryArithmetic.h>
#include "intDiv.h"
namespace DB
{
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
}
template <typename A, typename B>
struct DivideIntegralOrZeroImpl
{
@ -12,7 +20,13 @@ struct DivideIntegralOrZeroImpl
template <typename Result = ResultType>
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

View 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

View 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);