| // 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 "common/logging.h" | |
| #include "gen-cpp/Data_types.h" | |
| #include "gen-cpp/data_stream_service.pb.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. | |
| /// | |
| /// Values of this class may be unaligned so we mark it as "packed" so that the compiler | |
| /// does not assume proper alignment. If the compiler assumes that the value is aligned it | |
| /// may generate aligned load instructions (for example 'vmovdqa') which fail in case the | |
| /// value is actually misaligned. | |
| template<typename T> | |
| class __attribute__ ((packed)) DecimalValue { | |
| public: | |
| typedef T StorageType; | |
| DecimalValue() : value_(0) { } | |
| // Take 's' by value, not by reference: 's' is frequently an lvalue sourced from | |
| // a packed/tuple-backed struct (e.g. DecimalVal::val16), and binding a reference | |
| // to it can lose the "packed" provenance through inlining, causing the compiler | |
| // to emit an aligned SIMD load/store that segfaults on unaligned memory. | |
| DecimalValue(T s) : value_(s) { } | |
| DecimalValue& operator=(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 'value_pb'. | |
| static inline DecimalValue FromColumnValuePB(const ColumnValuePB& value_pb); | |
| 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). | |
| /// 'round' determines the behavior in case of scaling down, i.e. whether 11.56 should | |
| /// be 11.5 or 11.6. | |
| inline DecimalValue ScaleTo(int src_scale, int dst_scale, int dst_precision, | |
| bool round, 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 a copy of the underlying storage. We cannot return a reference or pointer | |
| /// because it may be unaligned (this class is packed) and unaligned pointers may lead | |
| /// to undefined behaviour. | |
| /// For a particular storage size, there is | |
| /// only one representation for any decimal and the storage is directly comparable. | |
| inline T value() const { return value_; } | |
| /// Sets the underlying storage to the given value. | |
| inline void set_value(T value) { | |
| value_ = 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 'value_pb'. | |
| void ToColumnValuePB(ColumnValuePB* value_pb) const { | |
| const uint8_t* data = reinterpret_cast<const uint8_t*>(&value_); | |
| value_pb->mutable_decimal_val()->assign(data, data + sizeof(T)); | |
| } | |
| 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 |