2012-12-24 08:16:25 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <DB/Functions/FunctionsArithmetic.h>
|
2015-05-14 12:08:27 +00:00
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <type_traits>
|
|
|
|
|
#include <array>
|
2012-12-24 08:16:25 +00:00
|
|
|
|
|
2016-01-16 00:45:19 +00:00
|
|
|
|
#if defined(__x86_64__)
|
|
|
|
|
#include <smmintrin.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-05-25 13:38:06 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
namespace DB
|
|
|
|
|
{
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
/** Функции округления:
|
|
|
|
|
* roundToExp2 - вниз до ближайшей степени двойки;
|
|
|
|
|
* roundDuration - вниз до ближайшего из: 0, 1, 10, 30, 60, 120, 180, 240, 300, 600, 1200, 1800, 3600, 7200, 18000, 36000;
|
|
|
|
|
* roundAge - вниз до ближайшего из: 0, 18, 25, 35, 45.
|
2016-01-16 00:45:19 +00:00
|
|
|
|
*
|
2015-05-25 13:35:04 +00:00
|
|
|
|
* round(x, N) - арифметическое округление (N = 0 по умолчанию).
|
|
|
|
|
* ceil(x, N) - наименьшее число, которое не меньше x (N = 0 по умолчанию).
|
|
|
|
|
* floor(x, N) - наибольшее число, которое не больше x (N = 0 по умолчанию).
|
|
|
|
|
*
|
|
|
|
|
* Значение параметра N:
|
|
|
|
|
* - N > 0: округлять до числа с N десятичными знаками после запятой
|
|
|
|
|
* - N < 0: окурглять до целого числа с N нулевыми знаками
|
|
|
|
|
* - N = 0: округлять до целого числа
|
2012-12-24 08:16:25 +00:00
|
|
|
|
*/
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
template<typename A>
|
|
|
|
|
struct RoundToExp2Impl
|
|
|
|
|
{
|
|
|
|
|
typedef A ResultType;
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
static inline A apply(A x)
|
|
|
|
|
{
|
|
|
|
|
return x <= 0 ? static_cast<A>(0) : (static_cast<A>(1) << static_cast<UInt64>(log2(static_cast<double>(x))));
|
|
|
|
|
}
|
|
|
|
|
};
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
template<>
|
|
|
|
|
struct RoundToExp2Impl<Float32>
|
|
|
|
|
{
|
|
|
|
|
typedef Float32 ResultType;
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
static inline Float32 apply(Float32 x)
|
|
|
|
|
{
|
|
|
|
|
return static_cast<Float32>(x < 1 ? 0. : pow(2., floor(log2(x))));
|
|
|
|
|
}
|
|
|
|
|
};
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
template<>
|
|
|
|
|
struct RoundToExp2Impl<Float64>
|
|
|
|
|
{
|
|
|
|
|
typedef Float64 ResultType;
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
static inline Float64 apply(Float64 x)
|
|
|
|
|
{
|
|
|
|
|
return x < 1 ? 0. : pow(2., floor(log2(x)));
|
|
|
|
|
}
|
|
|
|
|
};
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
template<typename A>
|
|
|
|
|
struct RoundDurationImpl
|
|
|
|
|
{
|
|
|
|
|
typedef UInt16 ResultType;
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
static inline ResultType apply(A x)
|
|
|
|
|
{
|
|
|
|
|
return x < 1 ? 0
|
|
|
|
|
: (x < 10 ? 1
|
|
|
|
|
: (x < 30 ? 10
|
|
|
|
|
: (x < 60 ? 30
|
|
|
|
|
: (x < 120 ? 60
|
|
|
|
|
: (x < 180 ? 120
|
|
|
|
|
: (x < 240 ? 180
|
|
|
|
|
: (x < 300 ? 240
|
|
|
|
|
: (x < 600 ? 300
|
|
|
|
|
: (x < 1200 ? 600
|
|
|
|
|
: (x < 1800 ? 1200
|
|
|
|
|
: (x < 3600 ? 1800
|
|
|
|
|
: (x < 7200 ? 3600
|
|
|
|
|
: (x < 18000 ? 7200
|
|
|
|
|
: (x < 36000 ? 18000
|
|
|
|
|
: 36000))))))))))))));
|
|
|
|
|
}
|
|
|
|
|
};
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
template<typename A>
|
|
|
|
|
struct RoundAgeImpl
|
|
|
|
|
{
|
|
|
|
|
typedef UInt8 ResultType;
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
static inline ResultType apply(A x)
|
|
|
|
|
{
|
2015-06-30 18:22:06 +00:00
|
|
|
|
return x < 1 ? 0
|
2015-06-30 18:38:55 +00:00
|
|
|
|
: (x < 18 ? 17
|
2012-12-24 08:16:25 +00:00
|
|
|
|
: (x < 25 ? 18
|
|
|
|
|
: (x < 35 ? 25
|
|
|
|
|
: (x < 45 ? 35
|
2015-06-30 18:22:06 +00:00
|
|
|
|
: 45))));
|
2012-12-24 08:16:25 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2015-05-27 22:59:46 +00:00
|
|
|
|
/** Быстрое вычисление остатка от деления для применения к округлению целых чисел.
|
|
|
|
|
* Без проверки, потому что делитель всегда положительный.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T, typename Enable = void>
|
2015-05-29 16:09:51 +00:00
|
|
|
|
struct FastModulo;
|
2015-05-27 22:59:46 +00:00
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
struct FastModulo<T, typename std::enable_if<std::is_integral<T>::value>::type>
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
template<typename InputType, typename Enable = void>
|
2015-05-29 16:09:51 +00:00
|
|
|
|
struct Extend;
|
2015-05-27 22:59:46 +00:00
|
|
|
|
|
|
|
|
|
template<typename InputType>
|
|
|
|
|
struct Extend<InputType,
|
|
|
|
|
typename std::enable_if<std::is_same<InputType, Int8>::value
|
|
|
|
|
|| std::is_same<InputType, Int16>::value>::type>
|
|
|
|
|
{
|
|
|
|
|
using Type = Int64;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename InputType>
|
|
|
|
|
struct Extend<InputType,
|
|
|
|
|
typename std::enable_if<std::is_same<InputType, UInt8>::value
|
|
|
|
|
|| std::is_same<InputType, UInt16>::value>::type>
|
|
|
|
|
{
|
|
|
|
|
using Type = UInt64;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename InputType>
|
|
|
|
|
struct Extend<InputType,
|
|
|
|
|
typename std::enable_if<std::is_integral<InputType>::value
|
|
|
|
|
&& (sizeof(InputType) >= 4)>::type>
|
|
|
|
|
{
|
|
|
|
|
using Type = InputType;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using U = typename Extend<T>::Type;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
using Divisor = std::pair<size_t, typename libdivide::divider<U> >;
|
|
|
|
|
|
|
|
|
|
static inline Divisor prepare(size_t b)
|
|
|
|
|
{
|
|
|
|
|
return std::make_pair(b, libdivide::divider<U>(b));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline T compute(T a, const Divisor & divisor)
|
|
|
|
|
{
|
|
|
|
|
U val = static_cast<U>(a);
|
|
|
|
|
U rem = val - (val / divisor.second) * static_cast<U>(divisor.first);
|
|
|
|
|
return static_cast<T>(rem);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
/** Этот параметр контролирует поведение функций округления.
|
|
|
|
|
*/
|
|
|
|
|
enum ScaleMode
|
|
|
|
|
{
|
|
|
|
|
PositiveScale, // округлять до числа с N десятичными знаками после запятой
|
|
|
|
|
NegativeScale, // окурглять до целого числа с N нулевыми знаками
|
|
|
|
|
ZeroScale, // округлять до целого числа
|
|
|
|
|
NullScale // возвращать нулевое значение
|
|
|
|
|
};
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
2016-01-16 01:18:42 +00:00
|
|
|
|
#if !defined(_MM_FROUND_NINT)
|
|
|
|
|
#define _MM_FROUND_NINT 0
|
|
|
|
|
#define _MM_FROUND_FLOOR 1
|
|
|
|
|
#define _MM_FROUND_CEIL 2
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
/** Реализация низкоуровневых функций округления для целочисленных значений.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T, int rounding_mode, ScaleMode scale_mode, typename Enable = void>
|
2015-05-29 16:09:51 +00:00
|
|
|
|
struct IntegerRoundingComputation;
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
template<typename T, int rounding_mode, ScaleMode scale_mode>
|
|
|
|
|
struct IntegerRoundingComputation<T, rounding_mode, scale_mode,
|
|
|
|
|
typename std::enable_if<std::is_integral<T>::value
|
|
|
|
|
&& ((scale_mode == PositiveScale) || (scale_mode == ZeroScale))>::type>
|
|
|
|
|
{
|
2015-05-27 22:59:46 +00:00
|
|
|
|
using Divisor = int;
|
|
|
|
|
|
|
|
|
|
static inline Divisor prepare(size_t scale)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline T compute(T in, const Divisor & scale)
|
2015-05-25 13:35:04 +00:00
|
|
|
|
{
|
|
|
|
|
return in;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
struct IntegerRoundingComputation<T, _MM_FROUND_NINT, NegativeScale,
|
|
|
|
|
typename std::enable_if<std::is_integral<T>::value>::type>
|
|
|
|
|
{
|
2015-05-27 22:59:46 +00:00
|
|
|
|
using Op = FastModulo<T>;
|
|
|
|
|
using Divisor = typename Op::Divisor;
|
|
|
|
|
|
|
|
|
|
static inline Divisor prepare(size_t scale)
|
2015-05-25 13:35:04 +00:00
|
|
|
|
{
|
2015-05-27 22:59:46 +00:00
|
|
|
|
return Op::prepare(scale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline T compute(T in, const Divisor & scale)
|
|
|
|
|
{
|
|
|
|
|
T factor = (in < 0) ? -1 : 1;
|
|
|
|
|
in *= factor;
|
|
|
|
|
T rem = Op::compute(in, scale);
|
2015-05-25 13:35:04 +00:00
|
|
|
|
in -= rem;
|
2015-05-27 22:59:46 +00:00
|
|
|
|
T res;
|
|
|
|
|
if ((2 * rem) < static_cast<T>(scale.first))
|
|
|
|
|
res = in;
|
2015-05-25 13:35:04 +00:00
|
|
|
|
else
|
2015-05-27 22:59:46 +00:00
|
|
|
|
res = in + scale.first;
|
|
|
|
|
return factor * res;
|
2015-05-25 13:35:04 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
struct IntegerRoundingComputation<T, _MM_FROUND_CEIL, NegativeScale,
|
|
|
|
|
typename std::enable_if<std::is_integral<T>::value>::type>
|
|
|
|
|
{
|
2015-05-27 22:59:46 +00:00
|
|
|
|
using Op = FastModulo<T>;
|
|
|
|
|
using Divisor = typename Op::Divisor;
|
|
|
|
|
|
|
|
|
|
static inline Divisor prepare(size_t scale)
|
2015-05-25 13:35:04 +00:00
|
|
|
|
{
|
2015-05-27 22:59:46 +00:00
|
|
|
|
return Op::prepare(scale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline T compute(T in, const Divisor & scale)
|
|
|
|
|
{
|
|
|
|
|
T factor = (in < 0) ? -1 : 1;
|
|
|
|
|
in *= factor;
|
|
|
|
|
T rem = Op::compute(in, scale);
|
|
|
|
|
T res = in - rem + scale.first;
|
|
|
|
|
return factor * res;
|
2015-05-25 13:35:04 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
struct IntegerRoundingComputation<T, _MM_FROUND_FLOOR, NegativeScale,
|
|
|
|
|
typename std::enable_if<std::is_integral<T>::value>::type>
|
|
|
|
|
{
|
2015-05-27 22:59:46 +00:00
|
|
|
|
using Op = FastModulo<T>;
|
|
|
|
|
using Divisor = typename Op::Divisor;
|
|
|
|
|
|
|
|
|
|
static inline Divisor prepare(size_t scale)
|
2015-05-25 13:35:04 +00:00
|
|
|
|
{
|
2015-05-27 22:59:46 +00:00
|
|
|
|
return Op::prepare(scale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline T compute(T in, const Divisor & scale)
|
|
|
|
|
{
|
|
|
|
|
T factor = (in < 0) ? -1 : 1;
|
|
|
|
|
in *= factor;
|
|
|
|
|
T rem = Op::compute(in, scale);
|
|
|
|
|
T res = in - rem;
|
|
|
|
|
return factor * res;
|
2015-05-25 13:35:04 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2016-01-16 00:45:19 +00:00
|
|
|
|
#if defined(__x86_64__)
|
|
|
|
|
template <typename T>
|
2015-06-03 17:01:30 +00:00
|
|
|
|
class BaseFloatRoundingComputation;
|
2015-05-25 13:35:04 +00:00
|
|
|
|
|
2016-01-16 00:45:19 +00:00
|
|
|
|
template <>
|
2015-06-03 17:01:30 +00:00
|
|
|
|
class BaseFloatRoundingComputation<Float32>
|
2015-05-14 12:08:27 +00:00
|
|
|
|
{
|
2015-06-03 17:01:30 +00:00
|
|
|
|
public:
|
2015-05-19 13:23:13 +00:00
|
|
|
|
using Scale = __m128;
|
2015-05-25 13:35:04 +00:00
|
|
|
|
static const size_t data_count = 4;
|
2015-06-03 17:01:30 +00:00
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
/// Предотвратить появление отрицательных нолей определённых в стандарте IEEE-754.
|
|
|
|
|
static inline void normalize(__m128 & val, const __m128 & mask)
|
|
|
|
|
{
|
|
|
|
|
__m128 mask1 = _mm_cmpeq_ps(val, getZero());
|
|
|
|
|
__m128 mask2 = _mm_and_ps(mask, mask1);
|
|
|
|
|
mask2 = _mm_cmpeq_ps(mask2, getZero());
|
|
|
|
|
mask2 = _mm_min_ps(mask2, getTwo());
|
|
|
|
|
mask2 = _mm_sub_ps(mask2, getOne());
|
|
|
|
|
val = _mm_mul_ps(val, mask2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline const __m128 & getZero()
|
|
|
|
|
{
|
|
|
|
|
static const __m128 zero = _mm_set1_ps(0.0);
|
|
|
|
|
return zero;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline const __m128 & getOne()
|
|
|
|
|
{
|
|
|
|
|
static const __m128 one = _mm_set1_ps(1.0);
|
|
|
|
|
return one;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline const __m128 & getTwo()
|
|
|
|
|
{
|
|
|
|
|
static const __m128 two = _mm_set1_ps(2.0);
|
|
|
|
|
return two;
|
|
|
|
|
}
|
2015-05-25 13:35:04 +00:00
|
|
|
|
};
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
2016-01-16 00:45:19 +00:00
|
|
|
|
template <>
|
2015-06-03 17:01:30 +00:00
|
|
|
|
class BaseFloatRoundingComputation<Float64>
|
2015-05-25 13:35:04 +00:00
|
|
|
|
{
|
2015-06-03 17:01:30 +00:00
|
|
|
|
public:
|
2015-05-25 13:35:04 +00:00
|
|
|
|
using Scale = __m128d;
|
|
|
|
|
static const size_t data_count = 2;
|
2015-06-03 17:01:30 +00:00
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
/// Предотвратить появление отрицательных нолей определённых в стандарте IEEE-754.
|
|
|
|
|
static inline void normalize(__m128d & val, const __m128d & mask)
|
|
|
|
|
{
|
|
|
|
|
__m128d mask1 = _mm_cmpeq_pd(val, getZero());
|
|
|
|
|
__m128d mask2 = _mm_and_pd(mask, mask1);
|
|
|
|
|
mask2 = _mm_cmpeq_pd(mask2, getZero());
|
|
|
|
|
mask2 = _mm_min_pd(mask2, getTwo());
|
|
|
|
|
mask2 = _mm_sub_pd(mask2, getOne());
|
|
|
|
|
val = _mm_mul_pd(val, mask2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline const __m128d & getZero()
|
|
|
|
|
{
|
|
|
|
|
static const __m128d zero = _mm_set1_pd(0.0);
|
|
|
|
|
return zero;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline const __m128d & getOne()
|
|
|
|
|
{
|
|
|
|
|
static const __m128d one = _mm_set1_pd(1.0);
|
|
|
|
|
return one;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline const __m128d & getTwo()
|
|
|
|
|
{
|
|
|
|
|
static const __m128d two = _mm_set1_pd(2.0);
|
|
|
|
|
return two;
|
|
|
|
|
}
|
2015-05-25 13:35:04 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Реализация низкоуровневых функций округления для значений с плавающей точкой.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T, int rounding_mode, ScaleMode scale_mode>
|
2015-06-03 17:01:30 +00:00
|
|
|
|
class FloatRoundingComputation;
|
2015-05-25 13:35:04 +00:00
|
|
|
|
|
|
|
|
|
template<int rounding_mode>
|
2015-06-03 17:01:30 +00:00
|
|
|
|
class FloatRoundingComputation<Float32, rounding_mode, PositiveScale>
|
2015-05-25 13:35:04 +00:00
|
|
|
|
: public BaseFloatRoundingComputation<Float32>
|
|
|
|
|
{
|
2015-06-03 17:01:30 +00:00
|
|
|
|
public:
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void prepare(size_t scale, Scale & mm_scale)
|
2015-05-14 12:08:27 +00:00
|
|
|
|
{
|
2015-05-19 13:23:13 +00:00
|
|
|
|
Float32 fscale = static_cast<Float32>(scale);
|
|
|
|
|
mm_scale = _mm_load1_ps(&fscale);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void compute(const Float32 * __restrict in, const Scale & scale, Float32 * __restrict out)
|
2015-05-25 13:35:04 +00:00
|
|
|
|
{
|
|
|
|
|
__m128 val = _mm_loadu_ps(in);
|
2015-06-03 17:01:30 +00:00
|
|
|
|
__m128 mask = _mm_cmplt_ps(val, getZero());
|
|
|
|
|
|
|
|
|
|
/// Алгоритм округления.
|
2015-05-25 13:35:04 +00:00
|
|
|
|
val = _mm_mul_ps(val, scale);
|
|
|
|
|
val = _mm_round_ps(val, rounding_mode);
|
|
|
|
|
val = _mm_div_ps(val, scale);
|
2015-06-03 17:01:30 +00:00
|
|
|
|
|
|
|
|
|
normalize(val, mask);
|
2015-05-25 13:35:04 +00:00
|
|
|
|
_mm_storeu_ps(out, val);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<int rounding_mode>
|
2015-06-03 17:01:30 +00:00
|
|
|
|
class FloatRoundingComputation<Float32, rounding_mode, NegativeScale>
|
2015-05-25 13:35:04 +00:00
|
|
|
|
: public BaseFloatRoundingComputation<Float32>
|
|
|
|
|
{
|
2015-06-03 17:01:30 +00:00
|
|
|
|
public:
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void prepare(size_t scale, Scale & mm_scale)
|
2015-05-20 15:34:09 +00:00
|
|
|
|
{
|
2015-05-25 13:35:04 +00:00
|
|
|
|
Float32 fscale = static_cast<Float32>(scale);
|
|
|
|
|
mm_scale = _mm_load1_ps(&fscale);
|
2015-05-20 15:34:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void compute(const Float32 * __restrict in, const Scale & scale, Float32 * __restrict out)
|
2015-05-19 13:23:13 +00:00
|
|
|
|
{
|
2015-05-25 13:35:04 +00:00
|
|
|
|
__m128 val = _mm_loadu_ps(in);
|
2015-06-03 17:01:30 +00:00
|
|
|
|
__m128 mask = _mm_cmplt_ps(val, getZero());
|
2015-05-27 22:59:46 +00:00
|
|
|
|
|
|
|
|
|
/// Превратить отрицательные значения в положительные.
|
|
|
|
|
__m128 factor = _mm_cmpge_ps(val, getZero());
|
|
|
|
|
factor = _mm_min_ps(factor, getTwo());
|
|
|
|
|
factor = _mm_sub_ps(factor, getOne());
|
|
|
|
|
val = _mm_mul_ps(val, factor);
|
|
|
|
|
|
|
|
|
|
/// Алгоритм округления.
|
2015-05-25 13:35:04 +00:00
|
|
|
|
val = _mm_div_ps(val, scale);
|
2015-05-25 15:04:21 +00:00
|
|
|
|
__m128 res = _mm_cmpge_ps(val, getOneTenth());
|
2015-05-25 13:35:04 +00:00
|
|
|
|
val = _mm_round_ps(val, rounding_mode);
|
|
|
|
|
val = _mm_mul_ps(val, scale);
|
|
|
|
|
val = _mm_and_ps(val, res);
|
2015-05-27 22:59:46 +00:00
|
|
|
|
|
|
|
|
|
/// Вернуть настоящие знаки всех значений.
|
|
|
|
|
val = _mm_mul_ps(val, factor);
|
|
|
|
|
|
2015-06-03 17:01:30 +00:00
|
|
|
|
normalize(val, mask);
|
2015-05-25 13:35:04 +00:00
|
|
|
|
_mm_storeu_ps(out, val);
|
2015-05-19 13:23:13 +00:00
|
|
|
|
}
|
2015-05-20 15:34:09 +00:00
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
private:
|
2015-05-25 15:04:21 +00:00
|
|
|
|
static inline const __m128 & getOneTenth()
|
2015-05-20 15:34:09 +00:00
|
|
|
|
{
|
2015-05-25 15:04:21 +00:00
|
|
|
|
static const __m128 one_tenth = _mm_set1_ps(0.1);
|
|
|
|
|
return one_tenth;
|
2015-05-20 15:34:09 +00:00
|
|
|
|
}
|
2015-05-19 13:23:13 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
template<int rounding_mode>
|
2015-06-03 17:01:30 +00:00
|
|
|
|
class FloatRoundingComputation<Float32, rounding_mode, ZeroScale>
|
2015-05-25 13:35:04 +00:00
|
|
|
|
: public BaseFloatRoundingComputation<Float32>
|
2015-05-19 13:23:13 +00:00
|
|
|
|
{
|
2015-06-03 17:01:30 +00:00
|
|
|
|
public:
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void prepare(size_t scale, Scale & mm_scale)
|
2015-05-25 13:35:04 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void compute(const Float32 * __restrict in, const Scale & scale, Float32 * __restrict out)
|
2015-05-25 13:35:04 +00:00
|
|
|
|
{
|
|
|
|
|
__m128 val = _mm_loadu_ps(in);
|
2015-06-03 17:01:30 +00:00
|
|
|
|
__m128 mask = _mm_cmplt_ps(val, getZero());
|
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
val = _mm_round_ps(val, rounding_mode);
|
2015-06-03 17:01:30 +00:00
|
|
|
|
|
|
|
|
|
normalize(val, mask);
|
2015-05-25 13:35:04 +00:00
|
|
|
|
_mm_storeu_ps(out, val);
|
|
|
|
|
}
|
|
|
|
|
};
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
template<int rounding_mode>
|
2015-06-03 17:01:30 +00:00
|
|
|
|
class FloatRoundingComputation<Float64, rounding_mode, PositiveScale>
|
2015-05-25 13:35:04 +00:00
|
|
|
|
: public BaseFloatRoundingComputation<Float64>
|
|
|
|
|
{
|
2015-06-03 17:01:30 +00:00
|
|
|
|
public:
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void prepare(size_t scale, Scale & mm_scale)
|
2015-05-19 13:23:13 +00:00
|
|
|
|
{
|
|
|
|
|
Float64 fscale = static_cast<Float64>(scale);
|
|
|
|
|
mm_scale = _mm_load1_pd(&fscale);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void compute(const Float64 * __restrict in, const Scale & scale, Float64 * __restrict out)
|
2015-05-25 13:35:04 +00:00
|
|
|
|
{
|
|
|
|
|
__m128d val = _mm_loadu_pd(in);
|
2015-06-03 17:01:30 +00:00
|
|
|
|
__m128d mask = _mm_cmplt_pd(val, getZero());
|
|
|
|
|
|
|
|
|
|
/// Алгоритм округления.
|
2015-05-25 13:35:04 +00:00
|
|
|
|
val = _mm_mul_pd(val, scale);
|
|
|
|
|
val = _mm_round_pd(val, rounding_mode);
|
|
|
|
|
val = _mm_div_pd(val, scale);
|
2015-06-03 17:01:30 +00:00
|
|
|
|
|
|
|
|
|
normalize(val, mask);
|
2015-05-25 13:35:04 +00:00
|
|
|
|
_mm_storeu_pd(out, val);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<int rounding_mode>
|
2015-06-03 17:01:30 +00:00
|
|
|
|
class FloatRoundingComputation<Float64, rounding_mode, NegativeScale>
|
2015-05-25 13:35:04 +00:00
|
|
|
|
: public BaseFloatRoundingComputation<Float64>
|
|
|
|
|
{
|
2015-06-03 17:01:30 +00:00
|
|
|
|
public:
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void prepare(size_t scale, Scale & mm_scale)
|
2015-05-20 15:34:09 +00:00
|
|
|
|
{
|
2015-05-25 13:35:04 +00:00
|
|
|
|
Float64 fscale = static_cast<Float64>(scale);
|
|
|
|
|
mm_scale = _mm_load1_pd(&fscale);
|
2015-05-20 15:34:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void compute(const Float64 * __restrict in, const Scale & scale, Float64 * __restrict out)
|
2015-05-19 13:23:13 +00:00
|
|
|
|
{
|
2015-05-25 13:35:04 +00:00
|
|
|
|
__m128d val = _mm_loadu_pd(in);
|
2015-06-03 17:01:30 +00:00
|
|
|
|
__m128d mask = _mm_cmplt_pd(val, getZero());
|
2015-05-27 22:59:46 +00:00
|
|
|
|
|
|
|
|
|
/// Превратить отрицательные значения в положительные.
|
|
|
|
|
__m128d factor = _mm_cmpge_pd(val, getZero());
|
|
|
|
|
factor = _mm_min_pd(factor, getTwo());
|
|
|
|
|
factor = _mm_sub_pd(factor, getOne());
|
|
|
|
|
val = _mm_mul_pd(val, factor);
|
|
|
|
|
|
|
|
|
|
/// Алгоритм округления.
|
2015-05-25 13:35:04 +00:00
|
|
|
|
val = _mm_div_pd(val, scale);
|
2015-05-25 15:04:21 +00:00
|
|
|
|
__m128d res = _mm_cmpge_pd(val, getOneTenth());
|
2015-05-25 13:35:04 +00:00
|
|
|
|
val = _mm_round_pd(val, rounding_mode);
|
|
|
|
|
val = _mm_mul_pd(val, scale);
|
|
|
|
|
val = _mm_and_pd(val, res);
|
2015-05-27 22:59:46 +00:00
|
|
|
|
|
|
|
|
|
/// Вернуть настоящие знаки всех значений.
|
|
|
|
|
val = _mm_mul_pd(val, factor);
|
|
|
|
|
|
2015-06-03 17:01:30 +00:00
|
|
|
|
normalize(val, mask);
|
2015-05-25 13:35:04 +00:00
|
|
|
|
_mm_storeu_pd(out, val);
|
2015-05-19 13:23:13 +00:00
|
|
|
|
}
|
2015-05-20 15:34:09 +00:00
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
private:
|
2015-05-25 15:04:21 +00:00
|
|
|
|
static inline const __m128d & getOneTenth()
|
2015-05-20 15:34:09 +00:00
|
|
|
|
{
|
2015-05-25 15:04:21 +00:00
|
|
|
|
static const __m128d one_tenth = _mm_set1_pd(0.1);
|
|
|
|
|
return one_tenth;
|
2015-05-20 15:34:09 +00:00
|
|
|
|
}
|
2015-05-19 13:23:13 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
template<int rounding_mode>
|
2015-06-03 17:01:30 +00:00
|
|
|
|
class FloatRoundingComputation<Float64, rounding_mode, ZeroScale>
|
2015-05-25 13:35:04 +00:00
|
|
|
|
: public BaseFloatRoundingComputation<Float64>
|
|
|
|
|
{
|
2015-06-03 17:01:30 +00:00
|
|
|
|
public:
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void prepare(size_t scale, Scale & mm_scale)
|
2015-05-25 13:35:04 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
static inline void compute(const Float64 * __restrict in, const Scale & scale, Float64 * __restrict out)
|
2015-05-25 13:35:04 +00:00
|
|
|
|
{
|
|
|
|
|
__m128d val = _mm_loadu_pd(in);
|
2015-06-03 17:01:30 +00:00
|
|
|
|
__m128d mask = _mm_cmplt_pd(val, getZero());
|
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
val = _mm_round_pd(val, rounding_mode);
|
2015-06-03 17:01:30 +00:00
|
|
|
|
|
|
|
|
|
normalize(val, mask);
|
2015-05-25 13:35:04 +00:00
|
|
|
|
_mm_storeu_pd(out, val);
|
|
|
|
|
}
|
|
|
|
|
};
|
2016-01-16 00:45:19 +00:00
|
|
|
|
#else
|
|
|
|
|
/// Реализация для ARM. Не векторизована. Не исправляет отрицательные нули.
|
|
|
|
|
|
|
|
|
|
template <int mode>
|
|
|
|
|
float roundWithMode(float x)
|
|
|
|
|
{
|
|
|
|
|
if (mode == _MM_FROUND_NINT) return roundf(x);
|
|
|
|
|
if (mode == _MM_FROUND_FLOOR) return floorf(x);
|
|
|
|
|
if (mode == _MM_FROUND_CEIL) return ceilf(x);
|
|
|
|
|
__builtin_unreachable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <int mode>
|
|
|
|
|
double roundWithMode(double x)
|
|
|
|
|
{
|
|
|
|
|
if (mode == _MM_FROUND_NINT) return round(x);
|
|
|
|
|
if (mode == _MM_FROUND_FLOOR) return floor(x);
|
|
|
|
|
if (mode == _MM_FROUND_CEIL) return ceil(x);
|
|
|
|
|
__builtin_unreachable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
class BaseFloatRoundingComputation
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
using Scale = T;
|
|
|
|
|
static const size_t data_count = 1;
|
|
|
|
|
|
|
|
|
|
static inline void prepare(size_t scale, Scale & mm_scale)
|
|
|
|
|
{
|
|
|
|
|
mm_scale = static_cast<T>(scale);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T, int rounding_mode, ScaleMode scale_mode>
|
|
|
|
|
class FloatRoundingComputation;
|
|
|
|
|
|
|
|
|
|
template <typename T, int rounding_mode>
|
|
|
|
|
class FloatRoundingComputation<T, rounding_mode, PositiveScale>
|
|
|
|
|
: public BaseFloatRoundingComputation<T>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
static inline void compute(const T * __restrict in, const T & scale, T * __restrict out)
|
|
|
|
|
{
|
|
|
|
|
out[0] = roundWithMode<rounding_mode>(in[0] * scale) / scale;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T, int rounding_mode>
|
|
|
|
|
class FloatRoundingComputation<T, rounding_mode, NegativeScale>
|
|
|
|
|
: public BaseFloatRoundingComputation<T>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
static inline void compute(const T * __restrict in, const T & scale, T * __restrict out)
|
|
|
|
|
{
|
|
|
|
|
out[0] = roundWithMode<rounding_mode>(in[0] / scale) * scale;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename T, int rounding_mode>
|
|
|
|
|
class FloatRoundingComputation<T, rounding_mode, ZeroScale>
|
|
|
|
|
: public BaseFloatRoundingComputation<T>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
static inline void prepare(size_t scale, T & mm_scale)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void compute(const T * __restrict in, const T & scale, T * __restrict out)
|
|
|
|
|
{
|
|
|
|
|
out[0] = roundWithMode<rounding_mode>(in[0]);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
/** Реализация высокоуровневых функций округления.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T, int rounding_mode, ScaleMode scale_mode, typename Enable = void>
|
2015-05-29 16:09:51 +00:00
|
|
|
|
struct FunctionRoundingImpl;
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
/** Реализация высокоуровневых функций округления для целочисленных значений.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T, int rounding_mode, ScaleMode scale_mode>
|
|
|
|
|
struct FunctionRoundingImpl<T, rounding_mode, scale_mode,
|
|
|
|
|
typename std::enable_if<std::is_integral<T>::value && (scale_mode != NullScale)>::type>
|
2015-05-19 13:23:13 +00:00
|
|
|
|
{
|
2015-05-25 13:35:04 +00:00
|
|
|
|
private:
|
|
|
|
|
using Op = IntegerRoundingComputation<T, rounding_mode, scale_mode>;
|
|
|
|
|
|
|
|
|
|
public:
|
2015-05-19 13:23:13 +00:00
|
|
|
|
static inline void apply(const PODArray<T> & in, size_t scale, typename ColumnVector<T>::Container_t & out)
|
|
|
|
|
{
|
2015-05-27 22:59:46 +00:00
|
|
|
|
auto divisor = Op::prepare(scale);
|
2015-05-28 12:58:09 +00:00
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
const T* begin_in = &in[0];
|
|
|
|
|
const T* end_in = begin_in + in.size();
|
2015-05-28 12:58:09 +00:00
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
T* __restrict p_out = &out[0];
|
|
|
|
|
for (const T* __restrict p_in = begin_in; p_in != end_in; ++p_in)
|
2015-05-28 12:58:09 +00:00
|
|
|
|
{
|
|
|
|
|
*p_out = Op::compute(*p_in, divisor);
|
|
|
|
|
++p_out;
|
|
|
|
|
}
|
2015-05-19 13:23:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline T apply(T val, size_t scale)
|
|
|
|
|
{
|
2015-05-27 22:59:46 +00:00
|
|
|
|
auto divisor = Op::prepare(scale);
|
|
|
|
|
return Op::compute(val, divisor);
|
2015-05-19 13:23:13 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
/** Реализация высокоуровневых функций округления для значений с плавающей точкой.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T, int rounding_mode, ScaleMode scale_mode>
|
|
|
|
|
struct FunctionRoundingImpl<T, rounding_mode, scale_mode,
|
|
|
|
|
typename std::enable_if<std::is_floating_point<T>::value && (scale_mode != NullScale)>::type>
|
2015-05-19 13:23:13 +00:00
|
|
|
|
{
|
|
|
|
|
private:
|
2015-05-25 13:35:04 +00:00
|
|
|
|
using Op = FloatRoundingComputation<T, rounding_mode, scale_mode>;
|
|
|
|
|
using Data = std::array<T, Op::data_count>;
|
2015-05-19 13:23:13 +00:00
|
|
|
|
using Scale = typename Op::Scale;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
static inline void apply(const PODArray<T> & in, size_t scale, typename ColumnVector<T>::Container_t & out)
|
|
|
|
|
{
|
|
|
|
|
Scale mm_scale;
|
2015-05-28 14:00:52 +00:00
|
|
|
|
Op::prepare(scale, mm_scale);
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
const size_t data_count = std::tuple_size<Data>();
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
const T* begin_in = &in[0];
|
|
|
|
|
const T* end_in = begin_in + in.size();
|
2015-05-28 12:58:09 +00:00
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
T* begin_out = &out[0];
|
|
|
|
|
const T* end_out = begin_out + out.size();
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
const T* limit = end_in - (data_count - 1);
|
|
|
|
|
|
|
|
|
|
const T* __restrict p_in = begin_in;
|
|
|
|
|
T* __restrict p_out = begin_out;
|
2015-05-28 12:58:09 +00:00
|
|
|
|
for (; p_in < limit; p_in += data_count)
|
|
|
|
|
{
|
2015-05-28 14:00:52 +00:00
|
|
|
|
Op::compute(p_in, mm_scale, p_out);
|
2015-05-28 12:58:09 +00:00
|
|
|
|
p_out += data_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (p_in < end_in)
|
2015-05-19 13:23:13 +00:00
|
|
|
|
{
|
2016-03-07 05:15:42 +00:00
|
|
|
|
Data tmp{{}};
|
2015-05-28 14:00:52 +00:00
|
|
|
|
T* begin_tmp = &tmp[0];
|
|
|
|
|
const T* end_tmp = begin_tmp + data_count;
|
2015-05-28 12:58:09 +00:00
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
for (T* __restrict p_tmp = begin_tmp; (p_tmp != end_tmp) && (p_in != end_in); ++p_tmp)
|
2015-05-28 12:58:09 +00:00
|
|
|
|
{
|
|
|
|
|
*p_tmp = *p_in;
|
|
|
|
|
++p_in;
|
|
|
|
|
}
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
|
|
|
|
Data res;
|
2015-05-28 14:00:52 +00:00
|
|
|
|
const T* begin_res = &res[0];
|
|
|
|
|
const T* end_res = begin_res + data_count;
|
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
Op::compute(reinterpret_cast<T *>(&tmp), mm_scale, reinterpret_cast<T *>(&res));
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
2015-05-28 14:00:52 +00:00
|
|
|
|
for (const T* __restrict p_res = begin_res; (p_res != end_res) && (p_out != end_out); ++p_res)
|
2015-05-28 12:58:09 +00:00
|
|
|
|
{
|
|
|
|
|
*p_out = *p_res;
|
|
|
|
|
++p_out;
|
|
|
|
|
}
|
2015-05-14 12:08:27 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-19 13:23:13 +00:00
|
|
|
|
static inline T apply(T val, size_t scale)
|
2015-05-14 12:08:27 +00:00
|
|
|
|
{
|
2015-05-19 13:23:13 +00:00
|
|
|
|
if (val == 0)
|
|
|
|
|
return val;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Scale mm_scale;
|
2015-05-28 14:00:52 +00:00
|
|
|
|
Op::prepare(scale, mm_scale);
|
2015-05-19 13:23:13 +00:00
|
|
|
|
|
2016-03-07 05:15:42 +00:00
|
|
|
|
Data tmp{{}};
|
2015-05-19 13:23:13 +00:00
|
|
|
|
tmp[0] = val;
|
|
|
|
|
|
|
|
|
|
Data res;
|
2015-05-25 13:35:04 +00:00
|
|
|
|
Op::compute(reinterpret_cast<T *>(&tmp), mm_scale, reinterpret_cast<T *>(&res));
|
2015-05-19 13:23:13 +00:00
|
|
|
|
return res[0];
|
|
|
|
|
}
|
2015-05-14 12:08:27 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
/** Реализация высокоуровневых функций округления в том случае, когда возвращается нулевое значение.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T, int rounding_mode, ScaleMode scale_mode>
|
|
|
|
|
struct FunctionRoundingImpl<T, rounding_mode, scale_mode,
|
|
|
|
|
typename std::enable_if<scale_mode == NullScale>::type>
|
2015-05-14 12:08:27 +00:00
|
|
|
|
{
|
2015-05-25 13:35:04 +00:00
|
|
|
|
public:
|
|
|
|
|
static inline void apply(const PODArray<T> & in, size_t scale, typename ColumnVector<T>::Container_t & out)
|
2015-05-14 12:08:27 +00:00
|
|
|
|
{
|
2015-05-29 16:46:46 +00:00
|
|
|
|
::memset(reinterpret_cast<T *>(&out[0]), 0, in.size() * sizeof(T));
|
2015-05-14 12:08:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
static inline T apply(T val, size_t scale)
|
2015-05-14 12:08:27 +00:00
|
|
|
|
{
|
2015-05-25 13:35:04 +00:00
|
|
|
|
return 0;
|
2015-05-14 12:08:27 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Следующий код генерирует во время сборки таблицу степеней числа 10.
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
/// Отдельные степени числа 10.
|
|
|
|
|
|
2015-05-19 13:26:23 +00:00
|
|
|
|
template<size_t N>
|
2015-05-14 12:08:27 +00:00
|
|
|
|
struct PowerOf10
|
|
|
|
|
{
|
|
|
|
|
static const size_t value = 10 * PowerOf10<N - 1>::value;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
struct PowerOf10<0>
|
|
|
|
|
{
|
|
|
|
|
static const size_t value = 1;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Объявление и определение контейнера содержащего таблицу степеней числа 10.
|
|
|
|
|
|
2015-05-19 13:26:23 +00:00
|
|
|
|
template<size_t... TArgs>
|
2015-05-14 12:08:27 +00:00
|
|
|
|
struct TableContainer
|
|
|
|
|
{
|
|
|
|
|
static const std::array<size_t, sizeof...(TArgs)> values;
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-19 13:26:23 +00:00
|
|
|
|
template<size_t... TArgs>
|
2016-03-07 06:08:34 +00:00
|
|
|
|
const std::array<size_t, sizeof...(TArgs)> TableContainer<TArgs...>::values {{ TArgs... }};
|
2015-05-14 12:08:27 +00:00
|
|
|
|
|
|
|
|
|
/// Генератор первых N степеней.
|
|
|
|
|
|
2015-05-19 13:26:23 +00:00
|
|
|
|
template<size_t N, size_t... TArgs>
|
2015-05-14 12:08:27 +00:00
|
|
|
|
struct FillArrayImpl
|
|
|
|
|
{
|
|
|
|
|
using result = typename FillArrayImpl<N - 1, PowerOf10<N>::value, TArgs...>::result;
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-19 13:26:23 +00:00
|
|
|
|
template<size_t... TArgs>
|
2015-05-14 12:08:27 +00:00
|
|
|
|
struct FillArrayImpl<0, TArgs...>
|
|
|
|
|
{
|
|
|
|
|
using result = TableContainer<PowerOf10<0>::value, TArgs...>;
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-19 13:26:23 +00:00
|
|
|
|
template<size_t N>
|
2015-05-14 12:08:27 +00:00
|
|
|
|
struct FillArray
|
|
|
|
|
{
|
2015-05-19 13:26:23 +00:00
|
|
|
|
using result = typename FillArrayImpl<N - 1>::result;
|
2015-05-14 12:08:27 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
/** Этот шаблон определяет точность, которую используют функции round/ceil/floor,
|
|
|
|
|
* затем преобразовывает её в значение, которое можно использовать в операциях
|
|
|
|
|
* умножения и деления. Поэтому оно называется масштабом.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T, typename U, typename Enable = void>
|
2015-05-29 16:09:51 +00:00
|
|
|
|
struct ScaleForRightType;
|
2015-05-25 13:35:04 +00:00
|
|
|
|
|
|
|
|
|
template<typename T, typename U>
|
|
|
|
|
struct ScaleForRightType<T, U,
|
|
|
|
|
typename std::enable_if<
|
|
|
|
|
std::is_floating_point<T>::value
|
|
|
|
|
&& std::is_signed<U>::value>::type>
|
|
|
|
|
{
|
|
|
|
|
static inline bool apply(const ColumnPtr & column, ScaleMode & scale_mode, size_t & scale)
|
|
|
|
|
{
|
|
|
|
|
using PowersOf10 = typename FillArray<std::numeric_limits<T>::digits10 + 1>::result;
|
|
|
|
|
using ColumnType = ColumnConst<U>;
|
|
|
|
|
|
|
|
|
|
const ColumnType * precision_col = typeid_cast<const ColumnType *>(&*column);
|
|
|
|
|
if (precision_col == nullptr)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
U val = precision_col->getData();
|
|
|
|
|
if (val < 0)
|
|
|
|
|
{
|
|
|
|
|
if (val < -static_cast<U>(std::numeric_limits<T>::digits10))
|
|
|
|
|
{
|
|
|
|
|
scale_mode = NullScale;
|
|
|
|
|
scale = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
scale_mode = NegativeScale;
|
|
|
|
|
scale = PowersOf10::values[-val];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (val == 0)
|
|
|
|
|
{
|
|
|
|
|
scale_mode = ZeroScale;
|
|
|
|
|
scale = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
scale_mode = PositiveScale;
|
|
|
|
|
if (val > std::numeric_limits<T>::digits10)
|
|
|
|
|
val = static_cast<U>(std::numeric_limits<T>::digits10);
|
|
|
|
|
scale = PowersOf10::values[val];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename T, typename U>
|
|
|
|
|
struct ScaleForRightType<T, U,
|
|
|
|
|
typename std::enable_if<
|
|
|
|
|
std::is_floating_point<T>::value
|
|
|
|
|
&& std::is_unsigned<U>::value>::type>
|
|
|
|
|
{
|
|
|
|
|
static inline bool apply(const ColumnPtr & column, ScaleMode & scale_mode, size_t & scale)
|
|
|
|
|
{
|
|
|
|
|
using PowersOf10 = typename FillArray<std::numeric_limits<T>::digits10 + 1>::result;
|
|
|
|
|
using ColumnType = ColumnConst<U>;
|
|
|
|
|
|
|
|
|
|
const ColumnType * precision_col = typeid_cast<const ColumnType *>(&*column);
|
|
|
|
|
if (precision_col == nullptr)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
U val = precision_col->getData();
|
|
|
|
|
if (val == 0)
|
|
|
|
|
{
|
|
|
|
|
scale_mode = ZeroScale;
|
|
|
|
|
scale = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
scale_mode = PositiveScale;
|
|
|
|
|
if (val > static_cast<U>(std::numeric_limits<T>::digits10))
|
|
|
|
|
val = static_cast<U>(std::numeric_limits<T>::digits10);
|
|
|
|
|
scale = PowersOf10::values[val];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename T, typename U>
|
|
|
|
|
struct ScaleForRightType<T, U,
|
|
|
|
|
typename std::enable_if<
|
|
|
|
|
std::is_integral<T>::value
|
|
|
|
|
&& std::is_signed<U>::value>::type>
|
|
|
|
|
{
|
|
|
|
|
static inline bool apply(const ColumnPtr & column, ScaleMode & scale_mode, size_t & scale)
|
|
|
|
|
{
|
|
|
|
|
using PowersOf10 = typename FillArray<std::numeric_limits<T>::digits10 + 1>::result;
|
|
|
|
|
using ColumnType = ColumnConst<U>;
|
|
|
|
|
|
|
|
|
|
const ColumnType * precision_col = typeid_cast<const ColumnType *>(&*column);
|
|
|
|
|
if (precision_col == nullptr)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
U val = precision_col->getData();
|
|
|
|
|
if (val < 0)
|
|
|
|
|
{
|
|
|
|
|
if (val < -std::numeric_limits<T>::digits10)
|
|
|
|
|
{
|
|
|
|
|
scale_mode = NullScale;
|
|
|
|
|
scale = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
scale_mode = NegativeScale;
|
|
|
|
|
scale = PowersOf10::values[-val];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
scale_mode = ZeroScale;
|
|
|
|
|
scale = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename T, typename U>
|
|
|
|
|
struct ScaleForRightType<T, U,
|
|
|
|
|
typename std::enable_if<
|
|
|
|
|
std::is_integral<T>::value
|
|
|
|
|
&& std::is_unsigned<U>::value>::type>
|
|
|
|
|
{
|
|
|
|
|
static inline bool apply(const ColumnPtr & column, ScaleMode & scale_mode, size_t & scale)
|
|
|
|
|
{
|
|
|
|
|
using ColumnType = ColumnConst<U>;
|
|
|
|
|
|
|
|
|
|
const ColumnType * precision_col = typeid_cast<const ColumnType *>(&*column);
|
|
|
|
|
if (precision_col == nullptr)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
scale_mode = ZeroScale;
|
|
|
|
|
scale = 1;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Превратить параметр точности в масштаб.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T>
|
|
|
|
|
struct ScaleForLeftType
|
|
|
|
|
{
|
|
|
|
|
static inline void apply(const ColumnPtr & column, ScaleMode & scale_mode, size_t & scale)
|
|
|
|
|
{
|
|
|
|
|
if (!( ScaleForRightType<T, UInt8>::apply(column, scale_mode, scale)
|
|
|
|
|
|| ScaleForRightType<T, UInt16>::apply(column, scale_mode, scale)
|
|
|
|
|
|| ScaleForRightType<T, UInt16>::apply(column, scale_mode, scale)
|
|
|
|
|
|| ScaleForRightType<T, UInt32>::apply(column, scale_mode, scale)
|
|
|
|
|
|| ScaleForRightType<T, UInt64>::apply(column, scale_mode, scale)
|
|
|
|
|
|| ScaleForRightType<T, Int8>::apply(column, scale_mode, scale)
|
|
|
|
|
|| ScaleForRightType<T, Int16>::apply(column, scale_mode, scale)
|
|
|
|
|
|| ScaleForRightType<T, Int32>::apply(column, scale_mode, scale)
|
|
|
|
|
|| ScaleForRightType<T, Int64>::apply(column, scale_mode, scale)
|
|
|
|
|
|| ScaleForRightType<T, Float32>::apply(column, scale_mode, scale)
|
|
|
|
|
|| ScaleForRightType<T, Float64>::apply(column, scale_mode, scale)))
|
|
|
|
|
{
|
|
|
|
|
throw Exception("Internal error", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Главный шаблон применяющий функцию округления к значению или столбцу.
|
|
|
|
|
*/
|
|
|
|
|
template<typename T, int rounding_mode, ScaleMode scale_mode>
|
|
|
|
|
struct Cruncher
|
|
|
|
|
{
|
|
|
|
|
using Op = FunctionRoundingImpl<T, rounding_mode, scale_mode>;
|
|
|
|
|
|
|
|
|
|
static inline void apply(Block & block, ColumnVector<T> * col, const ColumnNumbers & arguments, size_t result, size_t scale)
|
|
|
|
|
{
|
|
|
|
|
ColumnVector<T> * col_res = new ColumnVector<T>;
|
|
|
|
|
block.getByPosition(result).column = col_res;
|
|
|
|
|
|
|
|
|
|
typename ColumnVector<T>::Container_t & vec_res = col_res->getData();
|
|
|
|
|
vec_res.resize(col->getData().size());
|
|
|
|
|
|
|
|
|
|
Op::apply(col->getData(), scale, vec_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void apply(Block & block, ColumnConst<T> * col, const ColumnNumbers & arguments, size_t result, size_t scale)
|
|
|
|
|
{
|
|
|
|
|
T res = Op::apply(col->getData(), scale);
|
|
|
|
|
ColumnConst<T> * col_res = new ColumnConst<T>(col->size(), res);
|
|
|
|
|
block.getByPosition(result).column = col_res;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Выбрать подходящий алгоритм обработки в зависимости от масштаба.
|
|
|
|
|
*/
|
2016-01-16 00:45:19 +00:00
|
|
|
|
template<typename T, template <typename> class U, int rounding_mode>
|
2015-05-25 13:35:04 +00:00
|
|
|
|
struct Dispatcher
|
|
|
|
|
{
|
|
|
|
|
static inline void apply(Block & block, U<T> * col, const ColumnNumbers & arguments, size_t result)
|
|
|
|
|
{
|
|
|
|
|
ScaleMode scale_mode;
|
|
|
|
|
size_t scale;
|
|
|
|
|
|
|
|
|
|
if (arguments.size() == 2)
|
|
|
|
|
ScaleForLeftType<T>::apply(block.getByPosition(arguments[1]).column, scale_mode, scale);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
scale_mode = ZeroScale;
|
|
|
|
|
scale = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scale_mode == PositiveScale)
|
|
|
|
|
Cruncher<T, rounding_mode, PositiveScale>::apply(block, col, arguments, result, scale);
|
|
|
|
|
else if (scale_mode == ZeroScale)
|
|
|
|
|
Cruncher<T, rounding_mode, ZeroScale>::apply(block, col, arguments, result, scale);
|
|
|
|
|
else if (scale_mode == NegativeScale)
|
|
|
|
|
Cruncher<T, rounding_mode, NegativeScale>::apply(block, col, arguments, result, scale);
|
|
|
|
|
else if (scale_mode == NullScale)
|
|
|
|
|
Cruncher<T, rounding_mode, NullScale>::apply(block, col, arguments, result, scale);
|
|
|
|
|
else
|
|
|
|
|
throw Exception("Illegal operation", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Шаблон для функций, которые округляют значение входного параметра типа
|
|
|
|
|
* (U)Int8/16/32/64 или Float32/64, и принимают дополнительный необязятельный
|
|
|
|
|
* параметр (по умолчанию - 0).
|
2015-05-14 12:08:27 +00:00
|
|
|
|
*/
|
2015-05-19 13:23:13 +00:00
|
|
|
|
template<typename Name, int rounding_mode>
|
|
|
|
|
class FunctionRounding : public IFunction
|
2015-05-14 12:08:27 +00:00
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
static constexpr auto name = Name::name;
|
2015-05-19 13:23:13 +00:00
|
|
|
|
static IFunction * create(const Context & context) { return new FunctionRounding; }
|
2015-05-14 12:08:27 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
template<typename T>
|
|
|
|
|
bool checkType(const IDataType * type) const
|
|
|
|
|
{
|
|
|
|
|
return typeid_cast<const T *>(type) != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
bool executeForType(Block & block, const ColumnNumbers & arguments, size_t result)
|
|
|
|
|
{
|
|
|
|
|
if (ColumnVector<T> * col = typeid_cast<ColumnVector<T> *>(&*block.getByPosition(arguments[0]).column))
|
|
|
|
|
{
|
2015-05-25 13:35:04 +00:00
|
|
|
|
Dispatcher<T, ColumnVector, rounding_mode>::apply(block, col, arguments, result);
|
2015-05-14 12:08:27 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if (ColumnConst<T> * col = typeid_cast<ColumnConst<T> *>(&*block.getByPosition(arguments[0]).column))
|
|
|
|
|
{
|
2015-05-25 13:35:04 +00:00
|
|
|
|
Dispatcher<T, ColumnConst, rounding_mode>::apply(block, col, arguments, result);
|
2015-05-14 12:08:27 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2015-05-25 13:35:04 +00:00
|
|
|
|
else
|
|
|
|
|
return false;
|
2015-05-14 12:08:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
/// Получить имя функции.
|
|
|
|
|
String getName() const override
|
|
|
|
|
{
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Получить типы результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение.
|
|
|
|
|
DataTypePtr getReturnType(const DataTypes & arguments) const override
|
|
|
|
|
{
|
|
|
|
|
if ((arguments.size() < 1) || (arguments.size() > 2))
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if (arguments.size() == 2)
|
|
|
|
|
{
|
|
|
|
|
const IDataType * type = &*arguments[1];
|
|
|
|
|
if (!( checkType<DataTypeUInt8>(type)
|
|
|
|
|
|| checkType<DataTypeUInt16>(type)
|
|
|
|
|
|| checkType<DataTypeUInt32>(type)
|
|
|
|
|
|| checkType<DataTypeUInt64>(type)
|
|
|
|
|
|| checkType<DataTypeInt8>(type)
|
|
|
|
|
|| checkType<DataTypeInt16>(type)
|
|
|
|
|
|| checkType<DataTypeInt32>(type)
|
2015-05-25 13:35:04 +00:00
|
|
|
|
|| checkType<DataTypeInt64>(type)
|
|
|
|
|
|| checkType<DataTypeFloat32>(type)
|
|
|
|
|
|| checkType<DataTypeFloat64>(type)))
|
2015-05-14 12:08:27 +00:00
|
|
|
|
{
|
|
|
|
|
throw Exception("Illegal type in second argument of function " + getName(),
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const IDataType * type = &*arguments[0];
|
|
|
|
|
if (!type->behavesAsNumber())
|
|
|
|
|
throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(),
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
|
|
|
|
|
|
return arguments[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Выполнить функцию над блоком.
|
|
|
|
|
void execute(Block & block, const ColumnNumbers & arguments, size_t result) override
|
|
|
|
|
{
|
|
|
|
|
if (!( executeForType<UInt8>(block, arguments, result)
|
|
|
|
|
|| executeForType<UInt16>(block, arguments, result)
|
|
|
|
|
|| executeForType<UInt32>(block, arguments, result)
|
|
|
|
|
|| executeForType<UInt64>(block, arguments, result)
|
|
|
|
|
|| executeForType<Int8>(block, arguments, result)
|
|
|
|
|
|| executeForType<Int16>(block, arguments, result)
|
|
|
|
|
|| executeForType<Int32>(block, arguments, result)
|
|
|
|
|
|| executeForType<Int64>(block, arguments, result)
|
|
|
|
|
|| executeForType<Float32>(block, arguments, result)
|
|
|
|
|
|| executeForType<Float64>(block, arguments, result)))
|
|
|
|
|
{
|
|
|
|
|
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
|
|
|
|
|
+ " of argument of function " + getName(),
|
|
|
|
|
ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-11-29 08:06:29 +00:00
|
|
|
|
|
|
|
|
|
bool hasInformationAboutMonotonicity() const override
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-29 11:58:44 +00:00
|
|
|
|
Monotonicity getMonotonicityForRange(const IDataType & type, const Field & left, const Field & right) const override
|
2015-11-29 08:06:29 +00:00
|
|
|
|
{
|
2015-11-29 11:58:44 +00:00
|
|
|
|
return { true };
|
2015-11-29 08:06:29 +00:00
|
|
|
|
}
|
2015-05-14 12:08:27 +00:00
|
|
|
|
};
|
|
|
|
|
|
2014-11-12 17:23:26 +00:00
|
|
|
|
struct NameRoundToExp2 { static constexpr auto name = "roundToExp2"; };
|
|
|
|
|
struct NameRoundDuration { static constexpr auto name = "roundDuration"; };
|
|
|
|
|
struct NameRoundAge { static constexpr auto name = "roundAge"; };
|
2015-05-14 12:08:27 +00:00
|
|
|
|
struct NameRound { static constexpr auto name = "round"; };
|
|
|
|
|
struct NameCeil { static constexpr auto name = "ceil"; };
|
|
|
|
|
struct NameFloor { static constexpr auto name = "floor"; };
|
2014-11-12 17:23:26 +00:00
|
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
|
typedef FunctionUnaryArithmetic<RoundToExp2Impl, NameRoundToExp2> FunctionRoundToExp2;
|
|
|
|
|
typedef FunctionUnaryArithmetic<RoundDurationImpl, NameRoundDuration> FunctionRoundDuration;
|
|
|
|
|
typedef FunctionUnaryArithmetic<RoundAgeImpl, NameRoundAge> FunctionRoundAge;
|
2016-01-16 00:45:19 +00:00
|
|
|
|
|
2015-05-19 13:23:13 +00:00
|
|
|
|
typedef FunctionRounding<NameRound, _MM_FROUND_NINT> FunctionRound;
|
|
|
|
|
typedef FunctionRounding<NameFloor, _MM_FROUND_FLOOR> FunctionFloor;
|
2016-01-16 00:45:19 +00:00
|
|
|
|
typedef FunctionRounding<NameCeil, _MM_FROUND_CEIL> FunctionCeil;
|
2015-11-29 08:06:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct PositiveMonotonicity
|
|
|
|
|
{
|
|
|
|
|
static bool has() { return true; }
|
|
|
|
|
static IFunction::Monotonicity get(const Field & left, const Field & right)
|
|
|
|
|
{
|
2015-11-29 11:58:44 +00:00
|
|
|
|
return { true };
|
2015-11-29 08:06:29 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameRoundToExp2> : PositiveMonotonicity {};
|
|
|
|
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameRoundDuration> : PositiveMonotonicity {};
|
|
|
|
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameRoundAge> : PositiveMonotonicity {};
|
2012-12-24 08:16:25 +00:00
|
|
|
|
}
|