| /* |
| * 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 _IGNITE_COMMON_DECIMAL |
| #define _IGNITE_COMMON_DECIMAL |
| |
| #include <stdint.h> |
| #include <cctype> |
| |
| #include <sstream> |
| #include <iostream> |
| |
| #include <ignite/common/big_integer.h> |
| |
| namespace ignite |
| { |
| namespace common |
| { |
| /** |
| * Big decimal number implementation. |
| */ |
| class IGNITE_IMPORT_EXPORT Decimal |
| { |
| public: |
| /** |
| * Default constructor. |
| */ |
| Decimal(); |
| |
| /** |
| * Constructor. |
| * |
| * @param mag Bytes of the magnitude. Should be positive, sign is |
| * passed using separate argument. |
| * @param len Magnitude length in bytes. |
| * @param scale Scale. |
| * @param sign Sign of the decimal. Should be -1 for negative numbers |
| * and 1 otherwise. |
| * @param bigEndian If true then magnitude is in big-endian. Otherwise |
| * the byte order of the magnitude considered to be little-endian. |
| */ |
| Decimal(const int8_t* mag, int32_t len, int32_t scale, int32_t sign, bool bigEndian = true); |
| |
| /** |
| * Copy constructor. |
| * |
| * @param other Other instance. |
| */ |
| Decimal(const Decimal& other); |
| |
| /** |
| * Integer constructor. |
| * |
| * @param val Integer value. |
| */ |
| explicit Decimal(int64_t val); |
| |
| /** |
| * Integer constructor with scale. |
| * |
| * @param val Integer value. |
| * @param scale Scale. |
| */ |
| Decimal(int64_t val, int32_t scale); |
| |
| /** |
| * BigInteger constructor with scale. |
| * |
| * @param val BigInteger value. |
| * @param scale Scale. |
| */ |
| Decimal(const common::BigInteger& val, int32_t scale); |
| |
| /** |
| * String constructor. |
| * |
| * @param val String to assign. |
| * @param len String length. |
| */ |
| Decimal(const char* val, int32_t len); |
| |
| /** |
| * String constructor. |
| * |
| * @param val String to assign. |
| */ |
| explicit Decimal(const std::string& val) : |
| scale(0), |
| magnitude(0) |
| { |
| AssignString(val); |
| } |
| |
| /** |
| * Destructor. |
| */ |
| ~Decimal(); |
| |
| /** |
| * Copy operator. |
| * |
| * @param other Other instance. |
| * @return This. |
| */ |
| Decimal& operator=(const Decimal& other); |
| |
| /** |
| * Convert to double. |
| */ |
| operator double() const; |
| |
| /** |
| * Convert to int64_t. |
| */ |
| operator int64_t() const; |
| |
| /** |
| * Convert to double. |
| * |
| * @return Double value. |
| */ |
| double ToDouble() const; |
| |
| /** |
| * Convert to int64_t. |
| * |
| * @return int64_t value. |
| */ |
| int64_t ToInt64() const; |
| |
| /** |
| * Get scale. |
| * |
| * @return Scale. |
| */ |
| int32_t GetScale() const; |
| |
| /** |
| * Set scale. |
| * |
| * @param scale Scale to set. |
| * @param res Result is placed here. Can be *this. |
| */ |
| void SetScale(int32_t scale, Decimal& res) const; |
| |
| /** |
| * Get precision of the Decimal. |
| * |
| * @return Number of the decimal digits in the decimal representation |
| * of the value. |
| */ |
| int32_t GetPrecision() const; |
| |
| /** |
| * Get unscaled value. |
| * |
| * @return Unscaled value. |
| */ |
| const common::BigInteger& GetUnscaledValue() const; |
| |
| /** |
| * Swap function for the Decimal type. |
| * |
| * @param other Other instance. |
| */ |
| void Swap(Decimal& second); |
| |
| /** |
| * Get length of the magnitude. |
| * |
| * @return Length of the magnitude. |
| */ |
| int32_t GetMagnitudeLength() const; |
| |
| /** |
| * Assign specified value to this Decimal. |
| * |
| * @param val String to assign. |
| */ |
| void AssignString(const std::string& val) |
| { |
| AssignString(val.data(), static_cast<int32_t>(val.size())); |
| } |
| |
| /** |
| * Assign specified value to this Decimal. |
| * |
| * @param val String to assign. |
| * @param len String length. |
| */ |
| void AssignString(const char* val, int32_t len); |
| |
| /** |
| * Assign specified value to this Decimal. |
| * |
| * @param val Value to assign. |
| */ |
| void AssignInt64(int64_t val); |
| |
| /** |
| * Assign specified value to this Decimal. |
| * |
| * @param val Value to assign. |
| */ |
| void AssignDouble(double val); |
| |
| /** |
| * Assign specified value to this Decimal. |
| * |
| * @param val Value to assign. |
| */ |
| void AssignUint64(uint64_t val); |
| |
| /** |
| * Compare this instance to another. |
| * |
| * @param other Another instance. |
| * @return Comparasion result - 0 if equal, 1 if this is greater, -1 if |
| * this is less. |
| */ |
| int32_t Compare(const Decimal& other) const; |
| |
| /** |
| * Check whether this value is negative. |
| * |
| * @return True if this value is negative and false otherwise. |
| */ |
| bool IsNegative() const; |
| |
| /** |
| * Check whether this value is zero. |
| * |
| * @return True if this value is negative and false otherwise. |
| */ |
| bool IsZero() const; |
| |
| /** |
| * Check whether this value is positive. |
| * |
| * @return True if this value is positive and false otherwise. |
| */ |
| bool IsPositive() const; |
| |
| /** |
| * Output operator. |
| * |
| * @param os Output stream. |
| * @param val Value to output. |
| * @return Reference to the first param. |
| */ |
| friend std::ostream& operator<<(std::ostream& os, const Decimal& val) |
| { |
| const common::BigInteger& unscaled = val.GetUnscaledValue(); |
| |
| // Zero magnitude case. Scale does not matter. |
| if (unscaled.GetMagnitude().IsEmpty()) |
| return os << '0'; |
| |
| // Scale is zero or negative. No decimal point here. |
| if (val.scale <= 0) |
| { |
| os << unscaled; |
| |
| // Adding zeroes if needed. |
| for (int32_t i = 0; i < -val.scale; ++i) |
| os << '0'; |
| |
| return os; |
| } |
| |
| // Getting magnitude as a string. |
| std::stringstream converter; |
| |
| converter << unscaled; |
| |
| std::string magStr = converter.str(); |
| |
| int32_t magLen = static_cast<int32_t>(magStr.size()); |
| |
| int32_t magBegin = 0; |
| |
| // If value is negative passing minus sign. |
| if (magStr[magBegin] == '-') |
| { |
| os << magStr[magBegin]; |
| |
| ++magBegin; |
| --magLen; |
| } |
| |
| // Finding last non-zero char. There is no sense in trailing zeroes |
| // beyond the decimal point. |
| int32_t lastNonZero = static_cast<int32_t>(magStr.size()) - 1; |
| |
| while (lastNonZero >= magBegin && magStr[lastNonZero] == '0') |
| --lastNonZero; |
| |
| // This is expected as we already covered zero number case. |
| assert(lastNonZero >= magBegin); |
| |
| int32_t dotPos = magLen - val.scale; |
| |
| if (dotPos <= 0) |
| { |
| // Means we need to add leading zeroes. |
| os << '0' << '.'; |
| |
| while (dotPos < 0) |
| { |
| ++dotPos; |
| |
| os << '0'; |
| } |
| |
| os.write(&magStr[magBegin], lastNonZero - magBegin + 1); |
| } |
| else |
| { |
| // Decimal point is in the middle of the number. |
| // Just output everything before the decimal point. |
| os.write(&magStr[magBegin], dotPos); |
| |
| int32_t afterDot = lastNonZero - dotPos - magBegin + 1; |
| |
| if (afterDot > 0) |
| { |
| os << '.'; |
| |
| os.write(&magStr[magBegin + dotPos], afterDot); |
| } |
| } |
| |
| return os; |
| } |
| |
| /** |
| * Input operator. |
| * |
| * @param is Input stream. |
| * @param val Value to input. |
| * @return Reference to the first param. |
| */ |
| friend std::istream& operator>>(std::istream& is, Decimal& val) |
| { |
| std::istream::sentry sentry(is); |
| |
| // Return zero if input failed. |
| val.AssignInt64(0); |
| |
| if (!is) |
| return is; |
| |
| // Current char. |
| int c = is.peek(); |
| |
| // Current value parts. |
| uint64_t part = 0; |
| int32_t partDigits = 0; |
| int32_t scale = -1; |
| int32_t sign = 1; |
| |
| common::BigInteger& mag = val.magnitude; |
| common::BigInteger pow; |
| common::BigInteger bigPart; |
| |
| if (!is) |
| return is; |
| |
| // Checking sign. |
| if (c == '-' || c == '+') |
| { |
| if (c == '-') |
| sign = -1; |
| |
| is.ignore(); |
| c = is.peek(); |
| } |
| |
| // Reading number itself. |
| while (is) |
| { |
| if (isdigit(c)) |
| { |
| part = part * 10 + (c - '0'); |
| ++partDigits; |
| } |
| else if (c == '.' && scale < 0) |
| { |
| // We have found decimal point. Starting counting scale. |
| scale = 0; |
| } |
| else |
| break; |
| |
| is.ignore(); |
| c = is.peek(); |
| |
| if (part >= 1000000000000000000U) |
| { |
| common::BigInteger::GetPowerOfTen(partDigits, pow); |
| mag.Multiply(pow, mag); |
| |
| mag.Add(part); |
| |
| part = 0; |
| partDigits = 0; |
| } |
| |
| // Counting scale if the decimal point have been encountered. |
| if (scale >= 0) |
| ++scale; |
| } |
| |
| // Adding last part of the number. |
| if (partDigits) |
| { |
| common::BigInteger::GetPowerOfTen(partDigits, pow); |
| |
| mag.Multiply(pow, mag); |
| |
| mag.Add(part); |
| } |
| |
| // Adjusting scale. |
| if (scale < 0) |
| scale = 0; |
| else |
| --scale; |
| |
| // Reading exponent. |
| if (c == 'e' || c == 'E') |
| { |
| is.ignore(); |
| |
| int32_t exp = 0; |
| is >> exp; |
| |
| scale -= exp; |
| } |
| |
| val.scale = scale; |
| |
| if (sign < 0) |
| mag.Negate(); |
| |
| return is; |
| } |
| |
| private: |
| /** Scale. */ |
| int32_t scale; |
| |
| /** Magnitude. */ |
| common::BigInteger magnitude; |
| }; |
| |
| /** |
| * Comparison operator. |
| * |
| * @param val1 First value. |
| * @param val2 Second value. |
| * @return True if equal. |
| */ |
| IGNITE_IMPORT_EXPORT bool operator==(const Decimal& val1, const Decimal& val2); |
| |
| /** |
| * Comparison operator. |
| * |
| * @param val1 First value. |
| * @param val2 Second value. |
| * @return True if not equal. |
| */ |
| IGNITE_IMPORT_EXPORT bool operator!=(const Decimal& val1, const Decimal& val2); |
| |
| /** |
| * Comparison operator. |
| * |
| * @param val1 First value. |
| * @param val2 Second value. |
| * @return True if less. |
| */ |
| IGNITE_IMPORT_EXPORT bool operator<(const Decimal& val1, const Decimal& val2); |
| |
| /** |
| * Comparison operator. |
| * |
| * @param val1 First value. |
| * @param val2 Second value. |
| * @return True if less or equal. |
| */ |
| IGNITE_IMPORT_EXPORT bool operator<=(const Decimal& val1, const Decimal& val2); |
| |
| /** |
| * Comparison operator. |
| * |
| * @param val1 First value. |
| * @param val2 Second value. |
| * @return True if gretter. |
| */ |
| IGNITE_IMPORT_EXPORT bool operator>(const Decimal& val1, const Decimal& val2); |
| |
| /** |
| * Comparison operator. |
| * |
| * @param val1 First value. |
| * @param val2 Second value. |
| * @return True if gretter or equal. |
| */ |
| IGNITE_IMPORT_EXPORT bool operator>=(const Decimal& val1, const Decimal& val2); |
| } |
| } |
| |
| #endif //_IGNITE_COMMON_DECIMAL |