2012-12-24 08:16:25 +00:00
|
|
|
#pragma once
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
#include <Functions/FunctionHelpers.h>
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <IO/WriteHelpers.h>
|
2018-11-30 11:15:58 +00:00
|
|
|
#include <DataTypes/getLeastSupertype.h>
|
|
|
|
#include <DataTypes/DataTypeArray.h>
|
2018-12-27 00:10:38 +00:00
|
|
|
#include <DataTypes/DataTypesNumber.h>
|
2018-12-27 00:11:42 +00:00
|
|
|
#include <DataTypes/DataTypesDecimal.h>
|
2019-11-20 22:02:49 +00:00
|
|
|
#include <DataTypes/DataTypeDateTime64.h>
|
2018-12-27 00:09:39 +00:00
|
|
|
#include <Columns/ColumnVector.h>
|
2018-11-30 15:57:06 +00:00
|
|
|
#include <Interpreters/castColumn.h>
|
2019-12-09 13:12:54 +00:00
|
|
|
#include "IFunctionImpl.h"
|
2018-12-27 00:02:11 +00:00
|
|
|
#include <Common/intExp.h>
|
2019-08-21 02:28:04 +00:00
|
|
|
#include <Common/assert_cast.h>
|
2019-12-29 22:40:05 +00:00
|
|
|
#include <Core/Defines.h>
|
2015-05-14 12:08:27 +00:00
|
|
|
#include <cmath>
|
|
|
|
#include <type_traits>
|
|
|
|
#include <array>
|
2017-09-16 22:12:24 +00:00
|
|
|
#include <ext/bit_cast.h>
|
2018-11-30 15:57:06 +00:00
|
|
|
#include <algorithm>
|
2018-11-29 00:32:52 +00:00
|
|
|
|
2019-01-04 12:10:00 +00:00
|
|
|
#ifdef __SSE4_1__
|
2017-04-01 07:20:54 +00:00
|
|
|
#include <smmintrin.h>
|
2021-04-04 02:21:56 +00:00
|
|
|
#else
|
|
|
|
#include <fenv.h>
|
2016-01-16 00:45:19 +00:00
|
|
|
#endif
|
|
|
|
|
2017-09-16 22:12:24 +00:00
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
namespace DB
|
|
|
|
{
|
2014-11-12 17:23:26 +00:00
|
|
|
|
2017-06-13 02:06:53 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
2018-11-26 16:20:40 +00:00
|
|
|
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
2020-10-28 01:16:52 +00:00
|
|
|
extern const int ARGUMENT_OUT_OF_BOUND;
|
2018-11-21 10:10:22 +00:00
|
|
|
extern const int ILLEGAL_COLUMN;
|
2018-12-19 01:29:40 +00:00
|
|
|
extern const int BAD_ARGUMENTS;
|
2021-04-04 02:21:56 +00:00
|
|
|
extern const int CANNOT_SET_ROUNDING_MODE;
|
2017-06-13 02:06:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-27 15:45:25 +00:00
|
|
|
/** Rounding Functions:
|
2017-09-17 17:58:30 +00:00
|
|
|
* round(x, N) - rounding to nearest (N = 0 by default). Use banker's rounding for floating point numbers.
|
2019-12-13 14:50:14 +00:00
|
|
|
* roundBankers(x, N) - rounding to nearest (N = 0 by default). Use banker's rounding for all numbers.
|
2017-09-17 17:58:30 +00:00
|
|
|
* floor(x, N) is the largest number <= x (N = 0 by default).
|
|
|
|
* ceil(x, N) is the smallest number >= x (N = 0 by default).
|
|
|
|
* trunc(x, N) - is the largest by absolute value number that is not greater than x by absolute value (N = 0 by default).
|
2017-04-01 07:20:54 +00:00
|
|
|
*
|
2017-09-17 17:58:30 +00:00
|
|
|
* The value of the parameter N (scale):
|
2017-05-27 15:45:25 +00:00
|
|
|
* - N > 0: round to the number with N decimal places after the decimal point
|
|
|
|
* - N < 0: round to an integer with N zero characters
|
|
|
|
* - N = 0: round to an integer
|
2017-09-17 17:58:30 +00:00
|
|
|
*
|
|
|
|
* Type of the result is the type of argument.
|
|
|
|
* For integer arguments, when passing negative scale, overflow can occur.
|
|
|
|
* In that case, the behavior is implementation specific.
|
2017-04-01 07:20:54 +00:00
|
|
|
*/
|
2017-03-09 03:34:09 +00:00
|
|
|
|
|
|
|
|
2017-05-27 15:45:25 +00:00
|
|
|
/** This parameter controls the behavior of the rounding functions.
|
2017-09-16 16:38:27 +00:00
|
|
|
*/
|
|
|
|
enum class ScaleMode
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
Positive, // round to a number with N decimal places after the decimal point
|
|
|
|
Negative, // round to an integer with N zero characters
|
|
|
|
Zero, // round to an integer
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-19 13:23:13 +00:00
|
|
|
|
2017-09-16 16:38:27 +00:00
|
|
|
enum class RoundingMode
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2019-01-04 12:10:00 +00:00
|
|
|
#ifdef __SSE4_1__
|
2017-09-16 16:38:27 +00:00
|
|
|
Round = _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC,
|
|
|
|
Floor = _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC,
|
|
|
|
Ceil = _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC,
|
|
|
|
Trunc = _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC,
|
|
|
|
#else
|
|
|
|
Round = 8, /// Values are correspond to above just in case.
|
|
|
|
Floor = 9,
|
|
|
|
Ceil = 10,
|
|
|
|
Trunc = 11,
|
|
|
|
#endif
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-27 22:59:46 +00:00
|
|
|
|
2019-12-10 06:13:05 +00:00
|
|
|
enum class TieBreakingMode
|
|
|
|
{
|
|
|
|
Auto, // use banker's rounding for floating point numbers, round up otherwise
|
2019-12-11 02:35:47 +00:00
|
|
|
Bankers, // use banker's rounding
|
2019-12-10 06:13:05 +00:00
|
|
|
};
|
|
|
|
|
2020-10-28 01:16:52 +00:00
|
|
|
/// For N, no more than the number of digits in the largest type.
|
|
|
|
using Scale = Int16;
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 19:31:20 +00:00
|
|
|
/** Rounding functions for integer values.
|
2017-09-16 16:38:27 +00:00
|
|
|
*/
|
2019-12-10 06:13:05 +00:00
|
|
|
template <typename T, RoundingMode rounding_mode, ScaleMode scale_mode, TieBreakingMode tie_breaking_mode>
|
2017-09-16 16:38:27 +00:00
|
|
|
struct IntegerRoundingComputation
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
static const size_t data_count = 1;
|
|
|
|
|
|
|
|
static size_t prepare(size_t scale)
|
|
|
|
{
|
|
|
|
return scale;
|
|
|
|
}
|
|
|
|
|
2021-01-29 01:03:38 +00:00
|
|
|
/// Integer overflow is Ok.
|
|
|
|
static ALWAYS_INLINE_NO_SANITIZE_UNDEFINED T computeImpl(T x, T scale)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
switch (rounding_mode)
|
|
|
|
{
|
|
|
|
case RoundingMode::Trunc:
|
2017-09-16 19:31:20 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
return x / scale * scale;
|
2017-09-16 19:31:20 +00:00
|
|
|
}
|
2017-09-16 16:38:27 +00:00
|
|
|
case RoundingMode::Floor:
|
2017-09-16 19:31:20 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
if (x < 0)
|
|
|
|
x -= scale - 1;
|
|
|
|
return x / scale * scale;
|
2017-09-16 19:31:20 +00:00
|
|
|
}
|
2017-09-16 16:38:27 +00:00
|
|
|
case RoundingMode::Ceil:
|
2017-09-16 19:31:20 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
if (x >= 0)
|
|
|
|
x += scale - 1;
|
|
|
|
return x / scale * scale;
|
2017-09-16 19:31:20 +00:00
|
|
|
}
|
2017-09-16 16:38:27 +00:00
|
|
|
case RoundingMode::Round:
|
2017-09-16 19:31:20 +00:00
|
|
|
{
|
2019-12-17 02:57:11 +00:00
|
|
|
if (x < 0)
|
|
|
|
x -= scale;
|
2019-12-10 06:13:05 +00:00
|
|
|
switch (tie_breaking_mode)
|
|
|
|
{
|
|
|
|
case TieBreakingMode::Auto:
|
|
|
|
x = (x + scale / 2) / scale * scale;
|
2019-12-13 15:23:41 +00:00
|
|
|
break;
|
2019-12-11 02:35:47 +00:00
|
|
|
case TieBreakingMode::Bankers:
|
2019-12-10 06:13:05 +00:00
|
|
|
{
|
2019-12-13 15:23:41 +00:00
|
|
|
T quotient = (x + scale / 2) / scale;
|
|
|
|
if (quotient * scale == x + scale / 2)
|
2019-12-16 10:39:35 +00:00
|
|
|
// round half to even
|
2019-12-17 02:57:11 +00:00
|
|
|
x = ((quotient + (x < 0)) & ~1) * scale;
|
2019-12-13 15:23:41 +00:00
|
|
|
else
|
2019-12-16 10:39:35 +00:00
|
|
|
// round the others as usual
|
2019-12-13 15:23:41 +00:00
|
|
|
x = quotient * scale;
|
|
|
|
break;
|
2019-12-10 06:13:05 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-16 19:31:20 +00:00
|
|
|
return x;
|
|
|
|
}
|
2017-09-16 16:38:27 +00:00
|
|
|
}
|
2019-04-02 09:43:53 +00:00
|
|
|
|
|
|
|
__builtin_unreachable();
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
2018-12-24 00:47:11 +00:00
|
|
|
static ALWAYS_INLINE T compute(T x, T scale)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
switch (scale_mode)
|
|
|
|
{
|
|
|
|
case ScaleMode::Zero:
|
|
|
|
return x;
|
|
|
|
case ScaleMode::Positive:
|
|
|
|
return x;
|
|
|
|
case ScaleMode::Negative:
|
2017-09-16 19:31:20 +00:00
|
|
|
return computeImpl(x, scale);
|
2017-09-16 16:38:27 +00:00
|
|
|
}
|
2019-04-02 09:43:53 +00:00
|
|
|
|
|
|
|
__builtin_unreachable();
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-09-16 18:36:16 +00:00
|
|
|
|
2017-09-16 19:31:20 +00:00
|
|
|
static ALWAYS_INLINE void compute(const T * __restrict in, size_t scale, T * __restrict out)
|
2017-09-16 18:36:16 +00:00
|
|
|
{
|
2018-12-25 17:52:28 +00:00
|
|
|
if (sizeof(T) <= sizeof(scale) && scale > size_t(std::numeric_limits<T>::max()))
|
2018-12-24 00:47:11 +00:00
|
|
|
*out = 0;
|
|
|
|
else
|
|
|
|
*out = compute(*in, scale);
|
2017-09-16 18:36:16 +00:00
|
|
|
}
|
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-27 22:59:46 +00:00
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2019-01-04 12:10:00 +00:00
|
|
|
#ifdef __SSE4_1__
|
2017-09-16 18:36:16 +00:00
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
template <typename T>
|
|
|
|
class BaseFloatRoundingComputation;
|
|
|
|
|
|
|
|
template <>
|
|
|
|
class BaseFloatRoundingComputation<Float32>
|
|
|
|
{
|
|
|
|
public:
|
2017-09-16 18:36:16 +00:00
|
|
|
using ScalarType = Float32;
|
|
|
|
using VectorType = __m128;
|
2017-04-01 07:20:54 +00:00
|
|
|
static const size_t data_count = 4;
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType load(const ScalarType * in) { return _mm_loadu_ps(in); }
|
|
|
|
static VectorType load1(const ScalarType in) { return _mm_load1_ps(&in); }
|
|
|
|
static void store(ScalarType * out, VectorType val) { _mm_storeu_ps(out, val);}
|
|
|
|
static VectorType multiply(VectorType val, VectorType scale) { return _mm_mul_ps(val, scale); }
|
|
|
|
static VectorType divide(VectorType val, VectorType scale) { return _mm_div_ps(val, scale); }
|
|
|
|
template <RoundingMode mode> static VectorType apply(VectorType val) { return _mm_round_ps(val, int(mode)); }
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType prepare(size_t scale)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
return load1(scale);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-06-03 17:01:30 +00:00
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
template <>
|
|
|
|
class BaseFloatRoundingComputation<Float64>
|
|
|
|
{
|
|
|
|
public:
|
2017-09-16 18:36:16 +00:00
|
|
|
using ScalarType = Float64;
|
|
|
|
using VectorType = __m128d;
|
2017-04-01 07:20:54 +00:00
|
|
|
static const size_t data_count = 2;
|
2015-05-19 13:23:13 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType load(const ScalarType * in) { return _mm_loadu_pd(in); }
|
|
|
|
static VectorType load1(const ScalarType in) { return _mm_load1_pd(&in); }
|
|
|
|
static void store(ScalarType * out, VectorType val) { _mm_storeu_pd(out, val);}
|
|
|
|
static VectorType multiply(VectorType val, VectorType scale) { return _mm_mul_pd(val, scale); }
|
|
|
|
static VectorType divide(VectorType val, VectorType scale) { return _mm_div_pd(val, scale); }
|
|
|
|
template <RoundingMode mode> static VectorType apply(VectorType val) { return _mm_round_pd(val, int(mode)); }
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType prepare(size_t scale)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
return load1(scale);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
#else
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
/// Implementation for ARM. Not vectorized.
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
inline float roundWithMode(float x, RoundingMode mode)
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
switch (mode)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2021-04-04 02:21:56 +00:00
|
|
|
case RoundingMode::Round: return nearbyintf(x);
|
2017-09-16 18:36:16 +00:00
|
|
|
case RoundingMode::Floor: return floorf(x);
|
|
|
|
case RoundingMode::Ceil: return ceilf(x);
|
|
|
|
case RoundingMode::Trunc: return truncf(x);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2019-02-08 22:09:32 +00:00
|
|
|
|
|
|
|
__builtin_unreachable();
|
2017-09-16 18:36:16 +00:00
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
inline double roundWithMode(double x, RoundingMode mode)
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
switch (mode)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2021-04-04 02:21:56 +00:00
|
|
|
case RoundingMode::Round: return nearbyint(x);
|
2017-09-16 18:36:16 +00:00
|
|
|
case RoundingMode::Floor: return floor(x);
|
|
|
|
case RoundingMode::Ceil: return ceil(x);
|
|
|
|
case RoundingMode::Trunc: return trunc(x);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2019-02-08 22:09:32 +00:00
|
|
|
|
|
|
|
__builtin_unreachable();
|
2017-09-16 18:36:16 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
class BaseFloatRoundingComputation
|
|
|
|
{
|
|
|
|
public:
|
2017-09-16 18:36:16 +00:00
|
|
|
using ScalarType = T;
|
|
|
|
using VectorType = T;
|
2017-04-01 07:20:54 +00:00
|
|
|
static const size_t data_count = 1;
|
2017-03-09 03:34:09 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType load(const ScalarType * in) { return *in; }
|
|
|
|
static VectorType load1(const ScalarType in) { return in; }
|
|
|
|
static VectorType store(ScalarType * out, ScalarType val) { return *out = val;}
|
|
|
|
static VectorType multiply(VectorType val, VectorType scale) { return val * scale; }
|
|
|
|
static VectorType divide(VectorType val, VectorType scale) { return val / scale; }
|
|
|
|
template <RoundingMode mode> static VectorType apply(VectorType val) { return roundWithMode(val, mode); }
|
2016-01-16 00:45:19 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType prepare(size_t scale)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
return load1(scale);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2016-01-16 00:45:19 +00:00
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
/** Implementation of low-level round-off functions for floating-point values.
|
2017-09-16 16:38:27 +00:00
|
|
|
*/
|
|
|
|
template <typename T, RoundingMode rounding_mode, ScaleMode scale_mode>
|
2017-09-16 18:36:16 +00:00
|
|
|
class FloatRoundingComputation : public BaseFloatRoundingComputation<T>
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
using Base = BaseFloatRoundingComputation<T>;
|
2016-01-16 00:45:19 +00:00
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
public:
|
2017-09-16 18:36:16 +00:00
|
|
|
static inline void compute(const T * __restrict in, const typename Base::VectorType & scale, T * __restrict out)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
auto val = Base::load(in);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
if (scale_mode == ScaleMode::Positive)
|
|
|
|
val = Base::multiply(val, scale);
|
|
|
|
else if (scale_mode == ScaleMode::Negative)
|
|
|
|
val = Base::divide(val, scale);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
val = Base::template apply<rounding_mode>(val);
|
|
|
|
|
|
|
|
if (scale_mode == ScaleMode::Positive)
|
|
|
|
val = Base::divide(val, scale);
|
|
|
|
else if (scale_mode == ScaleMode::Negative)
|
|
|
|
val = Base::multiply(val, scale);
|
|
|
|
|
|
|
|
Base::store(out, val);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-19 13:23:13 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
|
|
|
|
/** Implementing high-level rounding functions.
|
2017-09-16 16:38:27 +00:00
|
|
|
*/
|
|
|
|
template <typename T, RoundingMode rounding_mode, ScaleMode scale_mode>
|
2017-09-16 20:17:19 +00:00
|
|
|
struct FloatRoundingImpl
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
|
|
|
private:
|
2020-08-19 11:52:17 +00:00
|
|
|
static_assert(!IsDecimalNumber<T>);
|
|
|
|
|
2017-09-16 20:17:19 +00:00
|
|
|
using Op = FloatRoundingComputation<T, rounding_mode, scale_mode>;
|
2017-04-01 07:20:54 +00:00
|
|
|
using Data = std::array<T, Op::data_count>;
|
2020-08-19 11:52:17 +00:00
|
|
|
using ColumnType = std::conditional_t<IsDecimalNumber<T>, ColumnDecimal<T>, ColumnVector<T>>;
|
|
|
|
using Container = typename ColumnType::Container;
|
2015-05-19 13:23:13 +00:00
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
public:
|
2020-08-19 11:52:17 +00:00
|
|
|
static NO_INLINE void apply(const Container & in, size_t scale, Container & out)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
auto mm_scale = Op::prepare(scale);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
const size_t data_count = std::tuple_size<Data>();
|
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
const T* end_in = in.data() + in.size();
|
|
|
|
const T* limit = in.data() + in.size() / data_count * data_count;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
const T* __restrict p_in = in.data();
|
|
|
|
T* __restrict p_out = out.data();
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
while (p_in < limit)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
Op::compute(p_in, mm_scale, p_out);
|
2017-09-16 18:36:16 +00:00
|
|
|
p_in += data_count;
|
2017-04-01 07:20:54 +00:00
|
|
|
p_out += data_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p_in < end_in)
|
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
Data tmp_src{{}};
|
|
|
|
Data tmp_dst;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
size_t tail_size_bytes = (end_in - p_in) * sizeof(*p_in);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
memcpy(&tmp_src, p_in, tail_size_bytes);
|
|
|
|
Op::compute(reinterpret_cast<T *>(&tmp_src), mm_scale, reinterpret_cast<T *>(&tmp_dst));
|
|
|
|
memcpy(p_out, &tmp_dst, tail_size_bytes);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-14 12:08:27 +00:00
|
|
|
|
2019-12-10 06:13:05 +00:00
|
|
|
template <typename T, RoundingMode rounding_mode, ScaleMode scale_mode, TieBreakingMode tie_breaking_mode>
|
2017-09-16 20:17:19 +00:00
|
|
|
struct IntegerRoundingImpl
|
|
|
|
{
|
|
|
|
private:
|
2019-12-10 06:13:05 +00:00
|
|
|
using Op = IntegerRoundingComputation<T, rounding_mode, scale_mode, tie_breaking_mode>;
|
2020-08-19 11:52:17 +00:00
|
|
|
using Container = typename ColumnVector<T>::Container;
|
2017-09-16 20:17:19 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
template <size_t scale>
|
2020-08-19 11:52:17 +00:00
|
|
|
static NO_INLINE void applyImpl(const Container & in, Container & out)
|
2017-09-16 20:17:19 +00:00
|
|
|
{
|
2018-12-25 17:45:02 +00:00
|
|
|
const T * end_in = in.data() + in.size();
|
2017-09-16 20:17:19 +00:00
|
|
|
|
2018-12-25 17:45:02 +00:00
|
|
|
const T * __restrict p_in = in.data();
|
|
|
|
T * __restrict p_out = out.data();
|
2017-09-16 20:17:19 +00:00
|
|
|
|
|
|
|
while (p_in < end_in)
|
|
|
|
{
|
|
|
|
Op::compute(p_in, scale, p_out);
|
|
|
|
++p_in;
|
|
|
|
++p_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 11:52:17 +00:00
|
|
|
static NO_INLINE void apply(const Container & in, size_t scale, Container & out)
|
2017-09-16 20:17:19 +00:00
|
|
|
{
|
|
|
|
/// Manual function cloning for compiler to generate integer division by constant.
|
|
|
|
switch (scale)
|
|
|
|
{
|
|
|
|
case 1ULL: return applyImpl<1ULL>(in, out);
|
|
|
|
case 10ULL: return applyImpl<10ULL>(in, out);
|
|
|
|
case 100ULL: return applyImpl<100ULL>(in, out);
|
|
|
|
case 1000ULL: return applyImpl<1000ULL>(in, out);
|
|
|
|
case 10000ULL: return applyImpl<10000ULL>(in, out);
|
|
|
|
case 100000ULL: return applyImpl<100000ULL>(in, out);
|
|
|
|
case 1000000ULL: return applyImpl<1000000ULL>(in, out);
|
|
|
|
case 10000000ULL: return applyImpl<10000000ULL>(in, out);
|
|
|
|
case 100000000ULL: return applyImpl<100000000ULL>(in, out);
|
|
|
|
case 1000000000ULL: return applyImpl<1000000000ULL>(in, out);
|
|
|
|
case 10000000000ULL: return applyImpl<10000000000ULL>(in, out);
|
|
|
|
case 100000000000ULL: return applyImpl<100000000000ULL>(in, out);
|
|
|
|
case 1000000000000ULL: return applyImpl<1000000000000ULL>(in, out);
|
|
|
|
case 10000000000000ULL: return applyImpl<10000000000000ULL>(in, out);
|
|
|
|
case 100000000000000ULL: return applyImpl<100000000000000ULL>(in, out);
|
|
|
|
case 1000000000000000ULL: return applyImpl<1000000000000000ULL>(in, out);
|
|
|
|
case 10000000000000000ULL: return applyImpl<10000000000000000ULL>(in, out);
|
|
|
|
case 100000000000000000ULL: return applyImpl<100000000000000000ULL>(in, out);
|
|
|
|
case 1000000000000000000ULL: return applyImpl<1000000000000000000ULL>(in, out);
|
|
|
|
case 10000000000000000000ULL: return applyImpl<10000000000000000000ULL>(in, out);
|
|
|
|
default:
|
2020-03-12 09:26:54 +00:00
|
|
|
throw Exception("Unexpected 'scale' parameter passed to function",
|
|
|
|
ErrorCodes::BAD_ARGUMENTS);
|
2017-09-16 20:17:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
|
2019-12-10 06:13:05 +00:00
|
|
|
template <typename T, RoundingMode rounding_mode, TieBreakingMode tie_breaking_mode>
|
|
|
|
class DecimalRoundingImpl
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2019-12-10 06:13:05 +00:00
|
|
|
private:
|
2020-08-19 11:52:17 +00:00
|
|
|
static_assert(IsDecimalNumber<T>);
|
|
|
|
|
2018-09-10 14:11:30 +00:00
|
|
|
using NativeType = typename T::NativeType;
|
2019-12-10 06:13:05 +00:00
|
|
|
using Op = IntegerRoundingComputation<NativeType, rounding_mode, ScaleMode::Negative, tie_breaking_mode>;
|
2018-09-10 13:52:18 +00:00
|
|
|
using Container = typename ColumnDecimal<T>::Container;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
public:
|
2020-10-28 01:16:52 +00:00
|
|
|
static NO_INLINE void apply(const Container & in, Container & out, Scale scale_arg)
|
2018-09-10 13:52:18 +00:00
|
|
|
{
|
|
|
|
scale_arg = in.getScale() - scale_arg;
|
|
|
|
if (scale_arg > 0)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-09-10 14:11:30 +00:00
|
|
|
size_t scale = intExp10(scale_arg);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
const NativeType * __restrict p_in = reinterpret_cast<const NativeType *>(in.data());
|
|
|
|
const NativeType * end_in = reinterpret_cast<const NativeType *>(in.data()) + in.size();
|
|
|
|
NativeType * __restrict p_out = reinterpret_cast<NativeType *>(out.data());
|
2017-09-17 17:58:30 +00:00
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
while (p_in < end_in)
|
|
|
|
{
|
|
|
|
Op::compute(p_in, scale, p_out);
|
|
|
|
++p_in;
|
|
|
|
++p_out;
|
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-09-10 13:52:18 +00:00
|
|
|
else
|
2020-08-19 11:52:17 +00:00
|
|
|
{
|
2021-01-26 19:39:03 +00:00
|
|
|
memcpy(out.data(), in.data(), in.size() * sizeof(T));
|
2020-08-19 11:52:17 +00:00
|
|
|
}
|
2018-09-10 13:52:18 +00:00
|
|
|
}
|
|
|
|
};
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
|
|
|
|
/** Select the appropriate processing algorithm depending on the scale.
|
|
|
|
*/
|
2019-12-10 06:13:05 +00:00
|
|
|
template <typename T, RoundingMode rounding_mode, TieBreakingMode tie_breaking_mode>
|
2018-09-10 13:52:18 +00:00
|
|
|
class Dispatcher
|
|
|
|
{
|
|
|
|
template <ScaleMode scale_mode>
|
|
|
|
using FunctionRoundingImpl = std::conditional_t<std::is_floating_point_v<T>,
|
|
|
|
FloatRoundingImpl<T, rounding_mode, scale_mode>,
|
2019-12-10 06:13:05 +00:00
|
|
|
IntegerRoundingImpl<T, rounding_mode, scale_mode, tie_breaking_mode>>;
|
2018-09-10 13:52:18 +00:00
|
|
|
|
2020-10-28 01:16:52 +00:00
|
|
|
static ColumnPtr apply(const ColumnVector<T> * col, Scale scale_arg)
|
2018-09-10 13:52:18 +00:00
|
|
|
{
|
2017-12-14 01:43:19 +00:00
|
|
|
auto col_res = ColumnVector<T>::create();
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2017-12-15 21:32:25 +00:00
|
|
|
typename ColumnVector<T>::Container & vec_res = col_res->getData();
|
2017-04-01 07:20:54 +00:00
|
|
|
vec_res.resize(col->getData().size());
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
if (!vec_res.empty())
|
2017-12-16 05:21:04 +00:00
|
|
|
{
|
2018-09-10 13:52:18 +00:00
|
|
|
if (scale_arg == 0)
|
|
|
|
{
|
|
|
|
size_t scale = 1;
|
|
|
|
FunctionRoundingImpl<ScaleMode::Zero>::apply(col->getData(), scale, vec_res);
|
|
|
|
}
|
|
|
|
else if (scale_arg > 0)
|
|
|
|
{
|
2018-09-10 14:13:03 +00:00
|
|
|
size_t scale = intExp10(scale_arg);
|
2018-09-10 13:52:18 +00:00
|
|
|
FunctionRoundingImpl<ScaleMode::Positive>::apply(col->getData(), scale, vec_res);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-09-10 14:13:03 +00:00
|
|
|
size_t scale = intExp10(-scale_arg);
|
2018-09-10 13:52:18 +00:00
|
|
|
FunctionRoundingImpl<ScaleMode::Negative>::apply(col->getData(), scale, vec_res);
|
|
|
|
}
|
2017-12-16 05:21:04 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
return col_res;
|
2018-09-10 13:52:18 +00:00
|
|
|
}
|
|
|
|
|
2020-10-28 01:16:52 +00:00
|
|
|
static ColumnPtr apply(const ColumnDecimal<T> * col, Scale scale_arg)
|
2018-09-10 13:52:18 +00:00
|
|
|
{
|
|
|
|
const typename ColumnDecimal<T>::Container & vec_src = col->getData();
|
|
|
|
|
|
|
|
auto col_res = ColumnDecimal<T>::create(vec_src.size(), vec_src.getScale());
|
|
|
|
auto & vec_res = col_res->getData();
|
|
|
|
|
|
|
|
if (!vec_res.empty())
|
2019-12-10 06:13:05 +00:00
|
|
|
DecimalRoundingImpl<T, rounding_mode, tie_breaking_mode>::apply(col->getData(), vec_res, scale_arg);
|
2017-12-16 05:21:04 +00:00
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
return col_res;
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-09-10 13:52:18 +00:00
|
|
|
|
|
|
|
public:
|
2020-10-28 01:16:52 +00:00
|
|
|
static ColumnPtr apply(const IColumn * column, Scale scale_arg)
|
2018-09-10 13:52:18 +00:00
|
|
|
{
|
2021-05-09 21:26:34 +00:00
|
|
|
if constexpr (is_arithmetic_v<T>)
|
2020-10-18 19:00:13 +00:00
|
|
|
return apply(checkAndGetColumn<ColumnVector<T>>(column), scale_arg);
|
2018-09-10 13:52:18 +00:00
|
|
|
else if constexpr (IsDecimalNumber<T>)
|
2020-10-18 19:00:13 +00:00
|
|
|
return apply(checkAndGetColumn<ColumnDecimal<T>>(column), scale_arg);
|
2018-09-10 13:52:18 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
|
|
|
|
2017-05-27 15:45:25 +00:00
|
|
|
/** A template for functions that round the value of an input parameter of type
|
2018-09-10 13:52:18 +00:00
|
|
|
* (U)Int8/16/32/64, Float32/64 or Decimal32/64/128, and accept an additional optional parameter (default is 0).
|
2017-09-16 18:36:16 +00:00
|
|
|
*/
|
2019-12-10 06:13:05 +00:00
|
|
|
template <typename Name, RoundingMode rounding_mode, TieBreakingMode tie_breaking_mode>
|
2017-03-09 03:34:09 +00:00
|
|
|
class FunctionRounding : public IFunction
|
|
|
|
{
|
|
|
|
public:
|
2017-04-01 07:20:54 +00:00
|
|
|
static constexpr auto name = Name::name;
|
2021-04-10 23:33:54 +00:00
|
|
|
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionRounding>(); }
|
2017-03-09 03:34:09 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
String getName() const override
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isVariadic() const override { return true; }
|
|
|
|
size_t getNumberOfArguments() const override { return 0; }
|
|
|
|
|
2017-05-27 15:45:25 +00:00
|
|
|
/// Get result types by argument types. If the function does not apply to these arguments, throw an exception.
|
2017-04-01 07:20:54 +00:00
|
|
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
|
|
|
{
|
2020-10-18 19:00:13 +00:00
|
|
|
if ((arguments.empty()) || (arguments.size() > 2))
|
2017-04-01 07:20:54 +00:00
|
|
|
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
|
|
|
|
+ toString(arguments.size()) + ", should be 1 or 2.",
|
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
for (const auto & type : arguments)
|
2019-05-24 12:11:03 +00:00
|
|
|
if (!isNumber(type))
|
2017-09-16 18:36:16 +00:00
|
|
|
throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(),
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
return arguments[0];
|
|
|
|
}
|
|
|
|
|
2020-11-17 13:24:45 +00:00
|
|
|
static Scale getScaleArg(const ColumnsWithTypeAndName & arguments)
|
2018-09-10 13:52:18 +00:00
|
|
|
{
|
|
|
|
if (arguments.size() == 2)
|
|
|
|
{
|
2020-10-18 19:00:13 +00:00
|
|
|
const IColumn & scale_column = *arguments[1].column;
|
2019-06-27 19:28:52 +00:00
|
|
|
if (!isColumnConst(scale_column))
|
2020-10-28 01:16:52 +00:00
|
|
|
throw Exception("Scale argument for rounding functions must be constant", ErrorCodes::ILLEGAL_COLUMN);
|
2018-09-10 13:52:18 +00:00
|
|
|
|
2019-08-21 02:28:04 +00:00
|
|
|
Field scale_field = assert_cast<const ColumnConst &>(scale_column).getField();
|
2018-09-10 13:52:18 +00:00
|
|
|
if (scale_field.getType() != Field::Types::UInt64
|
|
|
|
&& scale_field.getType() != Field::Types::Int64)
|
2020-10-28 01:16:52 +00:00
|
|
|
throw Exception("Scale argument for rounding functions must have integer type", ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
|
|
|
Int64 scale64 = scale_field.get<Int64>();
|
|
|
|
if (scale64 > std::numeric_limits<Scale>::max()
|
|
|
|
|| scale64 < std::numeric_limits<Scale>::min())
|
|
|
|
throw Exception("Scale argument for rounding function is too large", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
2018-09-10 13:52:18 +00:00
|
|
|
|
2020-10-28 01:16:52 +00:00
|
|
|
return scale64;
|
2018-09-10 13:52:18 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-23 08:40:43 +00:00
|
|
|
bool useDefaultImplementationForConstants() const override { return true; }
|
|
|
|
ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; }
|
|
|
|
|
2020-11-17 13:24:45 +00:00
|
|
|
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2020-10-18 19:00:13 +00:00
|
|
|
const ColumnWithTypeAndName & column = arguments[0];
|
2020-10-28 01:16:52 +00:00
|
|
|
Scale scale_arg = getScaleArg(arguments);
|
2018-09-10 13:52:18 +00:00
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
ColumnPtr res;
|
2018-09-10 13:52:18 +00:00
|
|
|
auto call = [&](const auto & types) -> bool
|
|
|
|
{
|
|
|
|
using Types = std::decay_t<decltype(types)>;
|
|
|
|
using DataType = typename Types::LeftType;
|
|
|
|
|
|
|
|
if constexpr (IsDataTypeNumber<DataType> || IsDataTypeDecimal<DataType>)
|
|
|
|
{
|
|
|
|
using FieldType = typename DataType::FieldType;
|
2020-10-18 19:00:13 +00:00
|
|
|
res = Dispatcher<FieldType, rounding_mode, tie_breaking_mode>::apply(column.column.get(), scale_arg);
|
2018-09-10 13:52:18 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2021-04-04 02:21:56 +00:00
|
|
|
#if !defined(__SSE4_1__)
|
|
|
|
/// In case of "nearbyint" function is used, we should ensure the expected rounding mode for the Banker's rounding.
|
|
|
|
/// Actually it is by default. But we will set it just in case.
|
|
|
|
|
|
|
|
if constexpr (rounding_mode == RoundingMode::Round)
|
|
|
|
if (0 != fesetround(FE_TONEAREST))
|
|
|
|
throw Exception("Cannot set floating point rounding mode", ErrorCodes::CANNOT_SET_ROUNDING_MODE);
|
|
|
|
#endif
|
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
if (!callOnIndexAndDataType<void>(column.type->getTypeId(), call))
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-09-10 13:52:18 +00:00
|
|
|
throw Exception("Illegal column " + column.name + " of argument of function " + getName(),
|
2017-04-01 07:20:54 +00:00
|
|
|
ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
}
|
2020-10-18 19:00:13 +00:00
|
|
|
|
|
|
|
return res;
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool hasInformationAboutMonotonicity() const override
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-02 02:47:12 +00:00
|
|
|
Monotonicity getMonotonicityForRange(const IDataType &, const Field &, const Field &) const override
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
PKCondition: infer index use with pk subexpression
By default only constraints explicitly matching
primary key expression (or expression wrapped in
a monotonic function) are eligible for part and
range selection. So for example, if index is:
(toStartOfHour(dt), UserID)
Then a query such as this resorts to full scan:
SELECT count() FROM t WHERE dt = now()
Intuitively, only parts with toStartOfHour(now())
could be selected, but it is less trivial to prove.
The primary key currently can be wrapped in a chain
of monotonic functions, so following would work:
toStartOfHour(dt) = toStartOfHour(now()) AND dt = now()
It must be however explicitly stated, if we wanted
to infer that we’d have to know the inverse function,
and prove that the inverse function is monotonic
on given interval. This is not practical as
there is no inverse function that for example undos
rounding, it isn’t strictly monotonic.
There are however functions that don’t transform
output range and preserve monotonicity on the
complete input range, such as rounding or casts
to a same or wider numeric type. This eliminates
the need to find inverse function, as no check for monotonicity over arbitrary interval is needed,
and thus makes this optimisation possible.
2017-07-06 05:39:05 +00:00
|
|
|
return { true, true, true };
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-14 12:08:27 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
|
2018-12-19 01:29:40 +00:00
|
|
|
/** Rounds down to a number within explicitly specified array.
|
|
|
|
* If the value is less than the minimal bound - returns the minimal bound.
|
|
|
|
*/
|
2018-12-03 11:28:22 +00:00
|
|
|
class FunctionRoundDown : public IFunction
|
|
|
|
{
|
2018-11-21 10:10:22 +00:00
|
|
|
public:
|
|
|
|
static constexpr auto name = "roundDown";
|
2021-04-10 23:33:54 +00:00
|
|
|
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionRoundDown>(); }
|
2018-11-21 10:10:22 +00:00
|
|
|
|
2018-12-03 11:28:22 +00:00
|
|
|
String getName() const override { return name; }
|
2018-11-21 10:10:22 +00:00
|
|
|
|
|
|
|
bool isVariadic() const override { return false; }
|
|
|
|
size_t getNumberOfArguments() const override { return 2; }
|
2018-12-19 01:29:40 +00:00
|
|
|
bool useDefaultImplementationForConstants() const override { return true; }
|
2018-11-21 10:10:22 +00:00
|
|
|
ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; }
|
|
|
|
|
2018-12-03 11:28:22 +00:00
|
|
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
|
|
|
{
|
2018-11-30 11:15:58 +00:00
|
|
|
const DataTypePtr & type_x = arguments[0];
|
|
|
|
|
2019-05-24 12:11:03 +00:00
|
|
|
if (!isNumber(type_x))
|
2018-11-30 11:15:58 +00:00
|
|
|
throw Exception{"Unsupported type " + type_x->getName()
|
|
|
|
+ " of first argument of function " + getName()
|
|
|
|
+ ", must be numeric type.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
|
|
|
|
|
|
|
const DataTypeArray * type_arr = checkAndGetDataType<DataTypeArray>(arguments[1].get());
|
|
|
|
|
|
|
|
if (!type_arr)
|
|
|
|
throw Exception{"Second argument of function " + getName()
|
|
|
|
+ ", must be array of boundaries to round to.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
|
|
|
|
|
|
|
const auto type_arr_nested = type_arr->getNestedType();
|
|
|
|
|
2019-05-24 12:11:03 +00:00
|
|
|
if (!isNumber(type_arr_nested))
|
2018-11-30 11:15:58 +00:00
|
|
|
{
|
|
|
|
throw Exception{"Elements of array of second argument of function " + getName()
|
|
|
|
+ " must be numeric type.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
|
|
|
}
|
|
|
|
return getLeastSupertype({type_x, type_arr_nested});
|
2018-11-29 00:32:52 +00:00
|
|
|
}
|
2018-11-21 10:10:22 +00:00
|
|
|
|
2020-11-17 13:24:45 +00:00
|
|
|
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t) const override
|
2018-12-03 11:28:22 +00:00
|
|
|
{
|
2020-10-18 19:00:13 +00:00
|
|
|
auto in_column = arguments[0].column;
|
|
|
|
const auto & in_type = arguments[0].type;
|
2018-11-30 11:15:58 +00:00
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
auto array_column = arguments[1].column;
|
|
|
|
const auto & array_type = arguments[1].type;
|
2018-12-19 02:43:09 +00:00
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
const auto & return_type = result_type;
|
2018-11-30 15:57:06 +00:00
|
|
|
auto column_result = return_type->createColumn();
|
2020-10-18 19:00:13 +00:00
|
|
|
auto * out = column_result.get();
|
2018-11-29 00:32:52 +00:00
|
|
|
|
2018-11-30 15:57:06 +00:00
|
|
|
if (!in_type->equals(*return_type))
|
2020-10-18 19:00:13 +00:00
|
|
|
in_column = castColumn(arguments[0], return_type);
|
2018-12-19 02:43:09 +00:00
|
|
|
|
|
|
|
if (!array_type->equals(*return_type))
|
2020-10-18 19:00:13 +00:00
|
|
|
array_column = castColumn(arguments[1], std::make_shared<DataTypeArray>(return_type));
|
2018-11-30 15:57:06 +00:00
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
const auto * in = in_column.get();
|
2018-12-19 02:43:09 +00:00
|
|
|
auto boundaries = typeid_cast<const ColumnConst &>(*array_column).getValue<Array>();
|
2018-12-19 01:29:40 +00:00
|
|
|
size_t num_boundaries = boundaries.size();
|
|
|
|
if (!num_boundaries)
|
|
|
|
throw Exception("Empty array is illegal for boundaries in " + getName() + " function", ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
|
2018-11-30 11:15:58 +00:00
|
|
|
if (!executeNum<UInt8>(in, out, boundaries)
|
|
|
|
&& !executeNum<UInt16>(in, out, boundaries)
|
|
|
|
&& !executeNum<UInt32>(in, out, boundaries)
|
|
|
|
&& !executeNum<UInt64>(in, out, boundaries)
|
|
|
|
&& !executeNum<Int8>(in, out, boundaries)
|
|
|
|
&& !executeNum<Int16>(in, out, boundaries)
|
|
|
|
&& !executeNum<Int32>(in, out, boundaries)
|
|
|
|
&& !executeNum<Int64>(in, out, boundaries)
|
|
|
|
&& !executeNum<Float32>(in, out, boundaries)
|
2018-12-19 02:11:31 +00:00
|
|
|
&& !executeNum<Float64>(in, out, boundaries)
|
|
|
|
&& !executeDecimal<Decimal32>(in, out, boundaries)
|
|
|
|
&& !executeDecimal<Decimal64>(in, out, boundaries)
|
|
|
|
&& !executeDecimal<Decimal128>(in, out, boundaries))
|
2018-11-30 11:15:58 +00:00
|
|
|
{
|
|
|
|
throw Exception{"Illegal column " + in->getName() + " of first argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN};
|
|
|
|
}
|
2018-11-29 00:32:52 +00:00
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
return column_result;
|
2018-11-29 00:32:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <typename T>
|
2020-07-21 13:58:07 +00:00
|
|
|
bool executeNum(const IColumn * in_untyped, IColumn * out_untyped, const Array & boundaries) const
|
2018-12-03 11:28:22 +00:00
|
|
|
{
|
2018-11-30 11:15:58 +00:00
|
|
|
const auto in = checkAndGetColumn<ColumnVector<T>>(in_untyped);
|
|
|
|
auto out = typeid_cast<ColumnVector<T> *>(out_untyped);
|
|
|
|
if (!in || !out)
|
|
|
|
return false;
|
2018-12-19 02:11:31 +00:00
|
|
|
|
2018-11-30 11:15:58 +00:00
|
|
|
executeImplNumToNum(in->getData(), out->getData(), boundaries);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2020-07-21 13:58:07 +00:00
|
|
|
bool executeDecimal(const IColumn * in_untyped, IColumn * out_untyped, const Array & boundaries) const
|
2018-12-19 02:11:31 +00:00
|
|
|
{
|
|
|
|
const auto in = checkAndGetColumn<ColumnDecimal<T>>(in_untyped);
|
|
|
|
auto out = typeid_cast<ColumnDecimal<T> *>(out_untyped);
|
|
|
|
if (!in || !out)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
executeImplNumToNum(in->getData(), out->getData(), boundaries);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Container>
|
2020-07-21 13:58:07 +00:00
|
|
|
void NO_INLINE executeImplNumToNum(const Container & src, Container & dst, const Array & boundaries) const
|
2018-12-03 11:28:22 +00:00
|
|
|
{
|
2018-12-19 02:11:31 +00:00
|
|
|
using ValueType = typename Container::value_type;
|
|
|
|
std::vector<ValueType> boundary_values(boundaries.size());
|
2018-12-03 11:28:22 +00:00
|
|
|
for (size_t i = 0; i < boundaries.size(); ++i)
|
2018-12-19 02:11:31 +00:00
|
|
|
boundary_values[i] = boundaries[i].get<ValueType>();
|
2018-12-19 01:29:40 +00:00
|
|
|
|
|
|
|
std::sort(boundary_values.begin(), boundary_values.end());
|
|
|
|
boundary_values.erase(std::unique(boundary_values.begin(), boundary_values.end()), boundary_values.end());
|
2018-11-30 15:57:06 +00:00
|
|
|
|
2018-11-21 10:10:22 +00:00
|
|
|
size_t size = src.size();
|
|
|
|
dst.resize(size);
|
2019-12-29 22:00:15 +00:00
|
|
|
|
2019-12-29 22:40:05 +00:00
|
|
|
if (boundary_values.size() < 32) /// Just a guess
|
2018-11-21 10:10:22 +00:00
|
|
|
{
|
2019-12-29 22:40:05 +00:00
|
|
|
/// Linear search with value on previous iteration as a hint.
|
|
|
|
/// Not optimal if the size of list is large and distribution of values is uniform random.
|
|
|
|
|
|
|
|
auto begin = boundary_values.begin();
|
|
|
|
auto end = boundary_values.end();
|
|
|
|
auto it = begin + (end - begin) / 2;
|
2019-12-29 22:00:15 +00:00
|
|
|
|
2019-12-29 22:40:05 +00:00
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-11-30 11:15:58 +00:00
|
|
|
{
|
2019-12-29 22:40:05 +00:00
|
|
|
auto value = src[i];
|
|
|
|
|
|
|
|
if (*it < value)
|
|
|
|
{
|
|
|
|
while (it != end && *it <= value)
|
|
|
|
++it;
|
|
|
|
if (it != begin)
|
|
|
|
--it;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (*it > value && it != begin)
|
|
|
|
--it;
|
|
|
|
}
|
|
|
|
|
|
|
|
dst[i] = *it;
|
2018-11-21 10:10:22 +00:00
|
|
|
}
|
2019-12-29 22:40:05 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-11-21 10:10:22 +00:00
|
|
|
{
|
2019-12-29 22:40:05 +00:00
|
|
|
auto it = std::upper_bound(boundary_values.begin(), boundary_values.end(), src[i]);
|
|
|
|
if (it == boundary_values.end())
|
|
|
|
{
|
|
|
|
dst[i] = boundary_values.back();
|
|
|
|
}
|
|
|
|
else if (it == boundary_values.begin())
|
|
|
|
{
|
|
|
|
dst[i] = boundary_values.front();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dst[i] = *(it - 1);
|
|
|
|
}
|
2018-11-21 10:10:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-07-30 19:47:32 +00:00
|
|
|
struct NameRound { static constexpr auto name = "round"; };
|
2019-12-13 14:50:14 +00:00
|
|
|
struct NameRoundBankers { static constexpr auto name = "roundBankers"; };
|
2017-07-30 19:47:32 +00:00
|
|
|
struct NameCeil { static constexpr auto name = "ceil"; };
|
|
|
|
struct NameFloor { static constexpr auto name = "floor"; };
|
2017-09-16 16:38:27 +00:00
|
|
|
struct NameTrunc { static constexpr auto name = "trunc"; };
|
2014-11-12 17:23:26 +00:00
|
|
|
|
2019-12-10 06:13:05 +00:00
|
|
|
using FunctionRound = FunctionRounding<NameRound, RoundingMode::Round, TieBreakingMode::Auto>;
|
2019-12-13 14:50:14 +00:00
|
|
|
using FunctionRoundBankers = FunctionRounding<NameRoundBankers, RoundingMode::Round, TieBreakingMode::Bankers>;
|
2019-12-10 06:13:05 +00:00
|
|
|
using FunctionFloor = FunctionRounding<NameFloor, RoundingMode::Floor, TieBreakingMode::Auto>;
|
|
|
|
using FunctionCeil = FunctionRounding<NameCeil, RoundingMode::Ceil, TieBreakingMode::Auto>;
|
|
|
|
using FunctionTrunc = FunctionRounding<NameTrunc, RoundingMode::Trunc, TieBreakingMode::Auto>;
|
2015-11-29 08:06:29 +00:00
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
}
|