blob: b34cde0e173591d049683c56d34ed900f6a0eca1 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#ifndef IMPALA_RUNTIME_DECIMAL_VALUE_H
#define IMPALA_RUNTIME_DECIMAL_VALUE_H
#include <ostream>
#include "gen-cpp/Data_types.h"
#include "runtime/multi-precision.h"
#include "runtime/types.h"
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
namespace impala {
/// Implementation of decimal types. The type is parametrized on the underlying
/// storage type, which must implement all operators (example of a storage type
/// is int32_t). The decimal does not store its precision and scale since we'd
/// like to keep the storage as small as possible.
/// Overflow handling: anytime the value is assigned, we need to consider overflow.
/// Overflow is handled by an output return parameter. Functions should set this
/// to true if overflow occured and leave it *unchanged* otherwise (e.g. |= rather than =).
/// This allows the caller to not have to check overflow after every call.
template<typename T>
class DecimalValue {
public:
typedef T StorageType;
DecimalValue() : value_(0) { }
DecimalValue(const T& s) : value_(s) { }
DecimalValue& operator=(const T& s) {
value_ = s;
return *this;
}
/// Returns the closest Decimal to 'd' of type 't', rounding to the nearest integer
/// if 'round' is true, truncating the decimal places otherwise
static inline DecimalValue FromDouble(const ColumnType& t, double d, bool round,
bool* overflow) {
return FromDouble(t.precision, t.scale, d, round, overflow);
}
/// Returns a new DecimalValue created from the value in 'tvalue'.
static inline DecimalValue FromTColumnValue(const TColumnValue& tvalue);
static inline DecimalValue FromDouble(int precision, int scale, double d,
bool round, bool* overflow);
/// Assigns *result as a decimal.
static inline DecimalValue FromInt(int precision, int scale, int64_t d, bool* overflow);
/// The overloaded operators assume that this and other have the same scale. They are
/// more efficient than the comparison functions that handle differing scale and should
/// be used if the scales are known to be the same. (e.g. min(decimal_col) or order by
/// decimal_col.
bool operator==(const DecimalValue& other) const {
return value_ == other.value_;
}
bool operator!=(const DecimalValue& other) const {
return value_ != other.value_;
}
bool operator<=(const DecimalValue& other) const {
return value_ <= other.value_;
}
bool operator<(const DecimalValue& other) const {
return value_ < other.value_;
}
bool operator>=(const DecimalValue& other) const {
return value_ >= other.value_;
}
bool operator>(const DecimalValue& other) const {
return value_ > other.value_;
}
DecimalValue operator-() const {
return DecimalValue(-value_);
}
bool is_negative() const { return value_ < 0; }
/// Compares this and other. Returns 0 if equal, < 0 if this < other and > 0 if
/// this > other.
inline int Compare(const DecimalValue& other) const;
/// Returns a new decimal scaled by from src_type to dst_type.
/// e.g. If this value was 1100 at scale 3 and the dst_type had scale two, the
/// result would be 110. (In both cases representing the decimal 1.1).
inline DecimalValue ScaleTo(int src_scale, int dst_scale, int dst_precision,
bool* overflow) const;
/// Implementations of the basic arithmetic operators. In all these functions,
/// we take the precision and scale of both inputs. The return type is assumed
/// to be known by the caller (generated by the planner).
/// Although these functions accept the result scale, that should be seen as
/// an optimization to avoid having to recompute it in the function. The
/// functions implement the SQL decimal rules *only* so other result scales are
/// not valid.
/// RESULT_T needs to be larger than T to avoid overflow issues.
template<typename RESULT_T>
inline DecimalValue<RESULT_T> Add(int this_scale, const DecimalValue& other,
int other_scale, int result_precision, int result_scale, bool round,
bool* overflow) const;
template<typename RESULT_T>
inline DecimalValue<RESULT_T> Subtract(int this_scale, const DecimalValue& other,
int other_scale, int result_precision, int result_scale, bool round,
bool* overflow) const {
return Add<RESULT_T>(this_scale, -other, other_scale, result_precision,
result_scale, round, overflow);
}
template<typename RESULT_T>
inline DecimalValue<RESULT_T> Multiply(int this_scale, const DecimalValue& other,
int other_scale, int result_precision, int result_scale, bool round,
bool* overflow) const;
/// is_nan is set to true if 'other' is 0. The value returned is undefined.
template<typename RESULT_T>
inline DecimalValue<RESULT_T> Divide(int this_scale, const DecimalValue& other,
int other_scale, int result_precision, int result_scale, bool round,
bool* is_nan, bool* overflow) const;
template<typename RESULT_T>
inline DecimalValue<RESULT_T> Mod(int this_scale, const DecimalValue& other,
int other_scale, int result_precision, int result_scale, bool round,
bool* is_nan, bool* overflow) const;
/// Compares this and other. Returns 0 if equal, < 0 if this < other and > 0 if
/// this > other.
inline int Compare(int this_scale, const DecimalValue& other, int other_scale) const;
/// Comparison utilities.
inline bool Eq(int this_scale, const DecimalValue& other, int other_scale) const {
return Compare(this_scale, other, other_scale) == 0;
}
inline bool Ne(int this_scale, const DecimalValue& other, int other_scale) const {
return Compare(this_scale, other, other_scale) != 0;
}
inline bool Ge(int this_scale, const DecimalValue& other, int other_scale) const {
return Compare(this_scale, other, other_scale) >= 0;
}
inline bool Gt(int this_scale, const DecimalValue& other, int other_scale) const {
return Compare(this_scale, other, other_scale) > 0;
}
inline bool Le(int this_scale, const DecimalValue& other, int other_scale) const {
return Compare(this_scale, other, other_scale) <= 0;
}
inline bool Lt(int this_scale, const DecimalValue& other, int other_scale) const {
return Compare(this_scale, other, other_scale) < 0;
}
/// Returns the underlying storage. For a particular storage size, there is
/// only one representation for any decimal and the storage is directly comparable.
inline const T& value() const { return value_; }
inline T& value() { return value_; }
/// Returns the value of the decimal before the decimal point.
inline const T whole_part(int scale) const;
/// Returns the value of the decimal after the decimal point.
inline const T fractional_part(int scale) const;
/// Returns the value as an integer, setting overflow to true on overflow,
/// and leaving unchanged otherwise. Rounds to the nearest integer, defined
/// as half / round away from zero. Template parameter RESULT_T should be a
/// UDF Val type which defines an integer underlying type as underlying_type_t
template <typename RESULT_T>
inline typename RESULT_T::underlying_type_t ToInt(int scale, bool* overflow) const;
/// Returns an approximate double for this decimal.
inline double ToDouble(int scale) const;
inline uint32_t Hash(int seed = 0) const;
std::string ToString(const ColumnType& type) const;
std::string ToString(int precision, int scale) const;
inline DecimalValue<T> Abs() const;
/// Store the binary representation of this DecimalValue in 'tvalue'.
void ToTColumnValue(TColumnValue* tvalue) const {
const uint8_t* data = reinterpret_cast<const uint8_t*>(&value_);
tvalue->decimal_val.assign(data, data + sizeof(T));
tvalue->__isset.decimal_val = true;
}
private:
T value_;
/// Returns in *x_val and *y_val, the adjusted values so that both are at
/// max(x_scale, y_scale) scale. It is the job of the caller to make sure
/// that both numbers can fit into RESULT_T after being scaled up.
template <typename RESULT_T>
static inline void AdjustToSameScale(const DecimalValue& x, int x_scale,
const DecimalValue& y, int y_scale, int result_precision, RESULT_T* x_scaled,
RESULT_T* y_scaled);
};
typedef DecimalValue<int32_t> Decimal4Value;
typedef DecimalValue<int64_t> Decimal8Value;
/// TODO: should we support Decimal12Value? We pad it to 16 bytes in the tuple
/// anyway.
typedef DecimalValue<int128_t> Decimal16Value;
inline std::ostream& operator<<(std::ostream& os, const Decimal4Value& d) {
return os << d.value();
}
inline std::ostream& operator<<(std::ostream& os, const Decimal8Value& d) {
return os << d.value();
}
inline std::ostream& operator<<(std::ostream& os, const Decimal16Value& d) {
return os << d.value();
}
}
#endif