ClickHouse/src/Core/DecimalFunctions.h

143 lines
4.9 KiB
C++
Raw Normal View History

2019-09-26 15:12:40 +00:00
#pragma once
// Moved Decimal-related functions out from Core/Types.h to reduce compilation time.
#include <Core/Types.h>
#include <Common/intExp.h>
#include <limits>
class DateLUTImpl;
2019-09-26 15:12:40 +00:00
namespace DB
{
namespace DecimalUtils
{
2019-09-26 15:12:40 +00:00
static constexpr size_t minPrecision() { return 1; }
template <typename T> static constexpr size_t maxPrecision() { return 0; }
template <> constexpr size_t maxPrecision<Decimal32>() { return 9; }
template <> constexpr size_t maxPrecision<Decimal64>() { return 18; }
template <> constexpr size_t maxPrecision<Decimal128>() { return 38; }
template <typename T> T scaleMultiplier(UInt32 scale);
template <> inline Int32 scaleMultiplier<Int32>(UInt32 scale) { return common::exp10_i32(scale); }
template <> inline Int64 scaleMultiplier<Int64>(UInt32 scale) { return common::exp10_i64(scale); }
template <> inline Int128 scaleMultiplier<Int128>(UInt32 scale) { return common::exp10_i128(scale); }
/** Components of DecimalX value:
* whole - represents whole part of decimal, can be negatve or positive.
* fractional - for fractional part of decimal, always positive.
*/
2019-09-26 15:12:40 +00:00
template <typename T>
struct DecimalComponents
{
T whole;
T fractional;
};
/** Make a decimal value from whole and fractional components with given scale multiplier.
* where scale_multiplier = scaleMultiplier<T>(scale)
* this is to reduce number of calls to scaleMultiplier when scale is known.
*
* Sign of `whole` controls sign of result: negative whole => negative result, positive whole => positive result.
* Sign of `fractional` is expected to be positive, otherwise result is undefined.
* If `scale` is to big (scale > maxPrecision<DecimalType::NativeType>), result is undefined.
*/
template <typename DecimalType>
2019-11-03 05:38:45 +00:00
DecimalType decimalFromComponentsWithMultiplier(const typename DecimalType::NativeType & whole,
const typename DecimalType::NativeType & fractional,
2019-11-03 05:38:45 +00:00
typename DecimalType::NativeType scale_multiplier)
{
using T = typename DecimalType::NativeType;
const auto fractional_sign = whole < 0 ? -1 : 1;
const T value = whole * scale_multiplier + fractional_sign * (fractional % scale_multiplier);
return DecimalType(value);
}
/** Make a decimal value from whole and fractional components with given scale.
*
* @see `decimalFromComponentsWithMultiplier` for details.
*/
template <typename DecimalType>
DecimalType decimalFromComponents(const typename DecimalType::NativeType & whole, const typename DecimalType::NativeType & fractional, UInt32 scale)
2019-09-26 15:12:40 +00:00
{
using T = typename DecimalType::NativeType;
return decimalFromComponentsWithMultiplier<DecimalType>(whole, fractional, scaleMultiplier<T>(scale));
2019-09-26 15:12:40 +00:00
}
/** Make a decimal value from whole and fractional components with given scale.
* @see `decimalFromComponentsWithMultiplier` for details.
*/
template <typename DecimalType>
DecimalType decimalFromComponents(const DecimalComponents<typename DecimalType::NativeType> & components, UInt32 scale)
2019-09-26 15:12:40 +00:00
{
return decimalFromComponents<DecimalType>(components.whole, components.fractional, scale);
2019-09-26 15:12:40 +00:00
}
/** Split decimal into whole and fractional parts with given scale_multiplier.
* This is an optimization to reduce number of calls to scaleMultiplier on known scale.
*/
template <typename DecimalType>
DecimalComponents<typename DecimalType::NativeType> splitWithScaleMultiplier(const DecimalType & decimal, typename DecimalType::NativeType scale_multiplier)
{
using T = typename DecimalType::NativeType;
const auto whole = decimal.value / scale_multiplier;
auto fractional = decimal.value % scale_multiplier;
if (fractional < T(0))
fractional *= T(-1);
return {whole, fractional};
}
/// Split decimal into components: whole and fractional part, @see `DecimalComponents` for details.
template <typename DecimalType>
DecimalComponents<typename DecimalType::NativeType> split(const DecimalType & decimal, UInt32 scale)
2019-09-26 15:12:40 +00:00
{
if (scale == 0)
{
return {decimal.value, 0};
}
return splitWithScaleMultiplier(decimal, scaleMultiplier<typename DecimalType::NativeType>(scale));
2019-09-26 15:12:40 +00:00
}
/** Get whole part from decimal.
*
* Sign of result follows sign of `decimal` value.
* If scale is to big, result is undefined.
*/
template <typename DecimalType>
typename DecimalType::NativeType getWholePart(const DecimalType & decimal, size_t scale)
2019-09-26 15:12:40 +00:00
{
if (scale == 0)
return decimal.value;
return decimal.value / scaleMultiplier<typename DecimalType::NativeType>(scale);
2019-09-26 15:12:40 +00:00
}
/** Get fractional part from decimal
*
* Result is always positive.
* If scale is to big, result is undefined.
*/
template <typename DecimalType>
typename DecimalType::NativeType getFractionalPart(const DecimalType & decimal, size_t scale)
2019-09-26 15:12:40 +00:00
{
using T = typename DecimalType::NativeType;
2019-09-26 15:12:40 +00:00
if (scale == 0)
return 0;
T result = decimal.value;
if (result < T(0))
result *= T(-1);
return result % scaleMultiplier<T>(scale);
2019-09-26 15:12:40 +00:00
}
}
2019-09-26 15:12:40 +00:00
}