ClickHouse/dbms/include/DB/Functions/NumberTraits.h

504 lines
23 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <boost/mpl/bool.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/greater.hpp>
#include <boost/mpl/min_max.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/comparison.hpp>
#include <DB/Core/Types.h>
#include <tuple>
namespace DB
{
/** Позволяет получить тип результата применения функций +, -, *, /, %, div (целочисленное деление).
* Правила отличаются от используемых в C++.
*/
namespace NumberTraits
{
using Unsigned = boost::mpl::false_ ;
using Signed = boost::mpl::true_ ;
using Integer = boost::mpl::false_ ;
using Floating = boost::mpl::true_ ;
using Bits0 = boost::mpl::int_<0> ;
using Bits8 = boost::mpl::int_<8> ;
using Bits16 = boost::mpl::int_<16> ;
using Bits32 = boost::mpl::int_<32> ;
using Bits64 = boost::mpl::int_<64> ;
using BitsTooMany = boost::mpl::int_<1024>;
struct Error {};
template <typename T> struct Next;
template <> struct Next<Bits0> { using Type = Bits0; };
template <> struct Next<Bits8> { using Type = Bits16; };
template <> struct Next<Bits16> { using Type = Bits32; };
template <> struct Next<Bits32> { using Type = Bits64; };
template <> struct Next<Bits64> { using Type = Bits64; };
template <typename T> struct ExactNext { using Type = typename Next<T>::Type; };
template <> struct ExactNext<Bits64> { using Type = BitsTooMany; };
template <typename T> struct Traits;
template <> struct Traits<void> { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits0 Bits; };
template <> struct Traits<UInt8> { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits8 Bits; };
template <> struct Traits<UInt16> { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits16 Bits; };
template <> struct Traits<UInt32> { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits32 Bits; };
template <> struct Traits<UInt64> { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits64 Bits; };
template <> struct Traits<Int8> { typedef Signed Sign; typedef Integer Floatness; typedef Bits8 Bits; };
template <> struct Traits<Int16> { typedef Signed Sign; typedef Integer Floatness; typedef Bits16 Bits; };
template <> struct Traits<Int32> { typedef Signed Sign; typedef Integer Floatness; typedef Bits32 Bits; };
template <> struct Traits<Int64> { typedef Signed Sign; typedef Integer Floatness; typedef Bits64 Bits; };
template <> struct Traits<Float32> { typedef Signed Sign; typedef Floating Floatness; typedef Bits32 Bits; };
template <> struct Traits<Float64> { typedef Signed Sign; typedef Floating Floatness; typedef Bits64 Bits; };
template <typename Sign, typename Floatness, typename Bits> struct Construct;
template <> struct Construct<Unsigned, Integer, Bits0> { using Type = void ; };
template <> struct Construct<Unsigned, Floating, Bits0> { using Type = void ; };
template <> struct Construct<Signed, Integer, Bits0> { using Type = void ; };
template <> struct Construct<Signed, Floating, Bits0> { using Type = void ; };
template <> struct Construct<Unsigned, Integer, Bits8> { using Type = UInt8 ; };
template <> struct Construct<Unsigned, Integer, Bits16> { using Type = UInt16 ; };
template <> struct Construct<Unsigned, Integer, Bits32> { using Type = UInt32 ; };
template <> struct Construct<Unsigned, Integer, Bits64> { using Type = UInt64 ; };
template <> struct Construct<Unsigned, Floating, Bits8> { using Type = Float32 ; };
template <> struct Construct<Unsigned, Floating, Bits16> { using Type = Float32 ; };
template <> struct Construct<Unsigned, Floating, Bits32> { using Type = Float32 ; };
template <> struct Construct<Unsigned, Floating, Bits64> { using Type = Float64 ; };
template <> struct Construct<Signed, Integer, Bits8> { using Type = Int8 ; };
template <> struct Construct<Signed, Integer, Bits16> { using Type = Int16 ; };
template <> struct Construct<Signed, Integer, Bits32> { using Type = Int32 ; };
template <> struct Construct<Signed, Integer, Bits64> { using Type = Int64 ; };
template <> struct Construct<Signed, Floating, Bits8> { using Type = Float32 ; };
template <> struct Construct<Signed, Floating, Bits16> { using Type = Float32 ; };
template <> struct Construct<Signed, Floating, Bits32> { using Type = Float32 ; };
template <> struct Construct<Signed, Floating, Bits64> { using Type = Float64 ; };
template <typename Sign, typename Floatness> struct Construct<Sign, Floatness, BitsTooMany> { using Type = Error; };
template <typename T>
inline bool isErrorType()
{
return false;
}
template <>
inline bool isErrorType<Error>()
{
return true;
}
/** Результат сложения или умножения вычисляется по следующим правилам:
* - если один из аргументов с плавающей запятой, то результат - с плавающей запятой, иначе - целый;
* - если одно из аргументов со знаком, то результат - со знаком, иначе - без знака;
* - результат содержит больше бит (не только значащих), чем максимум в аргументах
* (например, UInt8 + Int32 = Int64).
*/
template <typename A, typename B> struct ResultOfAdditionMultiplication
{
typedef typename Construct<
typename boost::mpl::or_<typename Traits<A>::Sign, typename Traits<B>::Sign>::type,
typename boost::mpl::or_<typename Traits<A>::Floatness, typename Traits<B>::Floatness>::type,
typename Next<typename boost::mpl::max<typename Traits<A>::Bits, typename Traits<B>::Bits>::type>::Type>::Type Type;
};
template <typename A, typename B> struct ResultOfSubtraction
{
typedef typename Construct<
Signed,
typename boost::mpl::or_<typename Traits<A>::Floatness, typename Traits<B>::Floatness>::type,
typename Next<typename boost::mpl::max<typename Traits<A>::Bits, typename Traits<B>::Bits>::type>::Type>::Type Type;
};
/** При делении всегда получается число с плавающей запятой.
*/
template <typename A, typename B> struct ResultOfFloatingPointDivision
{
using Type = Float64;
};
/** При целочисленном делении получается число, битность которого равна делимому.
*/
template <typename A, typename B> struct ResultOfIntegerDivision
{
typedef typename Construct<
typename boost::mpl::or_<typename Traits<A>::Sign, typename Traits<B>::Sign>::type,
Integer,
typename Traits<A>::Bits>::Type Type;
};
/** При взятии остатка получается число, битность которого равна делителю.
*/
template <typename A, typename B> struct ResultOfModulo
{
typedef typename Construct<
typename boost::mpl::or_<typename Traits<A>::Sign, typename Traits<B>::Sign>::type,
Integer,
typename Traits<B>::Bits>::Type Type;
};
template <typename A> struct ResultOfNegate
{
typedef typename Construct<
Signed,
typename Traits<A>::Floatness,
typename boost::mpl::if_<
typename Traits<A>::Sign,
typename Traits<A>::Bits,
typename Next<typename Traits<A>::Bits>::Type>::type>::Type Type;
};
template <typename A> struct ResultOfAbs
{
typedef typename Construct<
Unsigned,
typename Traits<A>::Floatness,
typename Traits <A>::Bits>::Type Type;
};
/** При побитовых операциях получается целое число, битность которого равна максимальной из битностей аргументов.
*/
template <typename A, typename B> struct ResultOfBit
{
typedef typename Construct<
typename boost::mpl::or_<typename Traits<A>::Sign, typename Traits<B>::Sign>::type,
Integer,
typename boost::mpl::max<
typename boost::mpl::if_<
typename Traits<A>::Floatness,
Bits64,
typename Traits<A>::Bits>::type,
typename boost::mpl::if_<
typename Traits<B>::Floatness,
Bits64,
typename Traits<B>::Bits>::type>::type>::Type Type;
};
template <typename A> struct ResultOfBitNot
{
typedef typename Construct<
typename Traits<A>::Sign,
Integer,
typename Traits<A>::Bits>::Type Type;
};
/** Приведение типов для функции if:
* 1) void, Type -> Type
* 2) UInt<x>, UInt<y> -> UInt<max(x,y)>
* 3) Int<x>, Int<y> -> Int<max(x,y)>
* 4) Float<x>, Float<y> -> Float<max(x, y)>
* 5) UInt<x>, Int<y> -> Int<max(x*2, y)>
* 6) Float<x>, [U]Int<y> -> Float<max(x, y*2)>
* 7) UInt64 , Int<x> -> Error
* 8) Float<x>, [U]Int64 -> Error
*/
template <typename A, typename B>
struct ResultOfIf
{
typedef
/// 1)
typename boost::mpl::if_<
typename boost::mpl::equal_to<typename Traits<A>::Bits, Bits0>::type,
B,
typename boost::mpl::if_<
typename boost::mpl::equal_to<typename Traits<B>::Bits, Bits0>::type,
A,
/// 4) и 6)
typename boost::mpl::if_<
typename boost::mpl::or_<
typename Traits<A>::Floatness,
typename Traits<B>::Floatness>::type,
typename Construct<
Signed,
Floating,
typename boost::mpl::max< /// Этот максимум нужен только потому что if_ всегда вычисляет все аргументы.
typename boost::mpl::max<
typename boost::mpl::if_<
typename Traits<A>::Floatness,
typename Traits<A>::Bits,
typename ExactNext<typename Traits<A>::Bits>::Type>::type,
typename boost::mpl::if_<
typename Traits<B>::Floatness,
typename Traits<B>::Bits,
typename ExactNext<typename Traits<B>::Bits>::Type>::type>::type,
Bits32>::type>::Type,
/// 2) и 3)
typename boost::mpl::if_<
typename boost::mpl::equal_to<
typename Traits<A>::Sign,
typename Traits<B>::Sign>::type,
typename boost::mpl::if_<
typename boost::mpl::less<
typename Traits<A>::Bits,
typename Traits<B>::Bits>::type,
B,
A>::type,
/// 5)
typename Construct<
Signed,
Integer,
typename boost::mpl::max<
typename boost::mpl::if_<
typename Traits<A>::Sign,
typename Traits<A>::Bits,
typename ExactNext<typename Traits<A>::Bits>::Type>::type,
typename boost::mpl::if_<
typename Traits<B>::Sign,
typename Traits<B>::Bits,
typename ExactNext<typename Traits<B>::Bits>::Type>::type>::type>::Type>::type>::type>::type>::type Type;
};
/** Перед применением оператора % и побитовых операций, операнды приводятся к целым числам. */
template <typename A> struct ToInteger
{
typedef typename Construct<
typename Traits<A>::Sign,
Integer,
typename boost::mpl::if_<
typename Traits<A>::Floatness,
Bits64,
typename Traits<A>::Bits>::type>::Type Type;
};
/// Notes on type composition.
///
/// I. Problem statement.
///
/// Type composition with ResultOfIf is not associative. Example:
/// (Int8 x UInt32) x Float32 = Int64 x Float32 = Error;
/// Int8 x (UInt32 x Float32) = Int8 x Float64 = Float64.
/// In order to sort out this issue, we design a slightly improved version
/// of ResultOfIf.
///
/// II. A more rigorous approach to ResultOfIf.
///
/// First we represent the set of types:
/// T = {Void,Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64,Float32,Float64}
/// as a poset P with the partial order being such that for any t1,t2 ∈ T,
/// t1 < t2 if and only if the domain of values of t1 is included in the domain
/// of values of t2.
///
/// For each type t ∈ T, we define C(t) as the set of chains of the poset P whose
/// unique minimal element is T.
///
/// Now for any two types t1,t2 ∈ T, we define the poset C(t1,t2) as the intersection
/// C(t1) ∩ C(t2).
///
/// Denote K(t1,t2) as the unique antichain of C(t1,t2) in which each element minimally
/// represents both t1 and t2. It is important to keep in mind that t1 and t2 are
/// *not* comparable.
///
/// For the most part, K(t1,t2) coincides with the result of the application of
/// ResultOfIf to t1 and t2. Nevertheless, for some particular combinations of t1
/// and t2, the map K returns one of the following two antichains: {Int32,Float32},
/// {Int64,Float64}.
///
/// From these observations, we conclude that the type system T and the composition
/// law ResultOfIf are not powerful enough to represent all the combinations of
/// elements of T. That is the reason why ResultOfIf is not associative.
///
/// III. Extending ResultOfIf.
///
/// Let's embed T into a larger set E of "enriched types" such that:
/// 1. E ⊂ TxT;
/// 2. for each t ∈ T, (T,Void) ∈ E.
/// 3. (Int32,Float32) ∈ E
/// 4. (Int64,Float64) ∈ E.
///
/// E represents the image A of the map K, a set of antichains, as a set of types.
///
/// Consider the canonical injection ψ : T x T ----> E x E and the natural bijection
/// φ : A ----> E.
/// Then there exists a unique map K' : E x E ----> E, that makes the diagram below
/// commutative:
///
/// K
/// T x T ----> A
/// | |
/// | ψ | φ
/// ↓ L ↓
/// E x E ----> E
///
/// L is exactly the same map as K, the sole difference being that L takes as
/// parameters extended types that map to ordinary ones.
///
/// Finally we extend the map L. To this end, we complete the type composition table
/// with the new types (Int32,Float32) and (Int64,Float64) appearing on either
/// the left-hand side or the right-hand side. This extended map is called
/// TypeProduct in the implementation. TypeProduct is both commutative and associative.
///
/// IV. Usage.
///
/// When we need to compose ordinary types, the following is to be performed:
/// 1. embed each type into its counterpart in E with EmbedType;
/// 2. compose the resulting enriched types with TypeProduct;
/// 3. return the first component of the result with ToOrdinaryType, which means
/// that, given an extended type e = (p,q) ∈ E, we return the ordinary type p ∈ T.
///
/// The result is the type we are looking for.
///
/// V. Example.
///
/// Suppose we need to compose, as in the problem statement, the types Int8,
/// UInt32, and Float32. The corresponding embedded types are:
/// (Int8,Void), (UInt32,Void), (Float32,Void).
///
/// By computing (Int8 x UInt32) x Float32, we get:
///
/// TypeProduct(TypeProduct((Int8,Void),(UInt32,Void)), (Float32,Void))
/// = TypeProduct((Int64,Float64), (Float32,Void))
/// = (Float64,void)
/// Thus, (Int8 x UInt32) x Float32 = Float64.
///
/// By computing Int8 x (UInt32 x Float32), we get:
///
/// TypeProduct((Int8,Void), TypeProduct((UInt32,Void), (Float32,Void)))
/// = TypeProduct((Int8,Void), (Float64,Void))
/// = (Float64,void)
/// Thus, Int8 x (UInt32 x Float32) = Float64.
///
namespace Enriched
{
/// Definitions of enriched types.
using Void = std::tuple<void, void>;
using Int8 = std::tuple<DB::Int8, void>;
using Int16 = std::tuple<DB::Int16, void>;
using Int32 = std::tuple<DB::Int32, void>;
using Int64 = std::tuple<DB::Int64, void>;
using UInt8 = std::tuple<DB::UInt8, void>;
using UInt16 = std::tuple<DB::UInt16, void>;
using UInt32 = std::tuple<DB::UInt32, void>;
using UInt64 = std::tuple<DB::UInt64, void>;
using Float32 = std::tuple<DB::Float32, void>;
using Float64 = std::tuple<DB::Float64, void>;
using IntFloat32 = std::tuple<DB::Int32, DB::Float32>;
using IntFloat64 = std::tuple<DB::Int64, DB::Float64>;
}
/// Embed an ordinary type into the corresponding enriched type.
template <typename T>
struct EmbedType;
template <> struct EmbedType<void> { using Type = Enriched::Void; };
template <> struct EmbedType<Int8> { using Type = Enriched::Int8; };
template <> struct EmbedType<Int16> { using Type = Enriched::Int16; };
template <> struct EmbedType<Int32> { using Type = Enriched::Int32; };
template <> struct EmbedType<Int64> { using Type = Enriched::Int64; };
template <> struct EmbedType<UInt8> { using Type = Enriched::UInt8; };
template <> struct EmbedType<UInt16> { using Type = Enriched::UInt16; };
template <> struct EmbedType<UInt32> { using Type = Enriched::UInt32; };
template <> struct EmbedType<UInt64> { using Type = Enriched::UInt64; };
template <> struct EmbedType<Float32> { using Type = Enriched::Float32; };
template <> struct EmbedType<Float64> { using Type = Enriched::Float64; };
/// Get an ordinary type from an enriched type.
template <typename TType>
struct ToOrdinaryType
{
using Type = typename std::tuple_element<0, TType>::type;
};
/// Get an ordinary type from an enriched type.
/// Error case.
template <>
struct ToOrdinaryType<Error>
{
using Type = Error;
};
/// Compute the product of two enriched numeric types.
template <typename T1, typename T2>
struct TypeProduct
{
using Type = Error;
};
/// Compute the product of two enriched numeric types.
/// Case when both of the source types and the resulting type map to ordinary types.
template <typename A, typename B>
struct TypeProduct<std::tuple<A, void>, std::tuple<B, void> >
{
private:
using Result = typename NumberTraits::ResultOfIf<A, B>::Type;
public:
using Type = typename std::conditional<
std::is_same<Result, Error>::value,
Error,
std::tuple<Result, void>
>::type;
};
/// Compute the product of two enriched numeric types.
/// Case when a source type or the resulting type does not map to any ordinary type.
template <> struct TypeProduct<Enriched::Int8, Enriched::UInt16> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::UInt16, Enriched::Int8> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::Int8, Enriched::UInt32> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::UInt32, Enriched::Int8> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::Int16, Enriched::UInt16> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::UInt16, Enriched::Int16> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::Int16, Enriched::UInt32> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::UInt32, Enriched::Int16> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::Int32, Enriched::UInt32> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::UInt32, Enriched::Int32> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::IntFloat32, Enriched::Int8> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::Int8, Enriched::IntFloat32> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::IntFloat32, Enriched::Int16> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::Int16, Enriched::IntFloat32> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::IntFloat32, Enriched::Int32> { using Type = Enriched::Int32; };
template <> struct TypeProduct<Enriched::Int32, Enriched::IntFloat32> { using Type = Enriched::Int32; };
template <> struct TypeProduct<Enriched::IntFloat32, Enriched::Int64> { using Type = Enriched::Int64; };
template <> struct TypeProduct<Enriched::Int64, Enriched::IntFloat32> { using Type = Enriched::Int64; };
template <> struct TypeProduct<Enriched::IntFloat32, Enriched::Float32> { using Type = Enriched::Float32; };
template <> struct TypeProduct<Enriched::Float32, Enriched::IntFloat32> { using Type = Enriched::Float32; };
template <> struct TypeProduct<Enriched::IntFloat32, Enriched::Float64> { using Type = Enriched::Float64; };
template <> struct TypeProduct<Enriched::Float64, Enriched::IntFloat32> { using Type = Enriched::Float64; };
template <> struct TypeProduct<Enriched::IntFloat32, Enriched::UInt8> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::UInt8, Enriched::IntFloat32> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::IntFloat32, Enriched::UInt16> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::UInt16, Enriched::IntFloat32> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::IntFloat32, Enriched::UInt32> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::UInt32, Enriched::IntFloat32> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::IntFloat32, Enriched::IntFloat32> { using Type = Enriched::IntFloat32; };
template <> struct TypeProduct<Enriched::IntFloat32, Enriched::IntFloat64> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::IntFloat64, Enriched::IntFloat32> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::IntFloat64, Enriched::Int8> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::Int8, Enriched::IntFloat64> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::IntFloat64, Enriched::Int16> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::Int16, Enriched::IntFloat64> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::IntFloat64, Enriched::Int32> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::Int32, Enriched::IntFloat64> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::IntFloat64, Enriched::Int64> { using Type = Enriched::Int64; };
template <> struct TypeProduct<Enriched::Int64, Enriched::IntFloat64> { using Type = Enriched::Int64; };
template <> struct TypeProduct<Enriched::IntFloat64, Enriched::Float32> { using Type = Enriched::Float64; };
template <> struct TypeProduct<Enriched::Float32, Enriched::IntFloat64> { using Type = Enriched::Float64; };
template <> struct TypeProduct<Enriched::IntFloat64, Enriched::Float64> { using Type = Enriched::Float64; };
template <> struct TypeProduct<Enriched::Float64, Enriched::IntFloat64> { using Type = Enriched::Float64; };
template <> struct TypeProduct<Enriched::IntFloat64, Enriched::UInt8> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::UInt8, Enriched::IntFloat64> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::IntFloat64, Enriched::UInt16> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::UInt16, Enriched::IntFloat64> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::IntFloat64, Enriched::UInt32> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::UInt32, Enriched::IntFloat64> { using Type = Enriched::IntFloat64; };
template <> struct TypeProduct<Enriched::IntFloat64, Enriched::IntFloat64> { using Type = Enriched::IntFloat64; };
}
}