| /* |
| * 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. |
| */ |
| |
| #pragma once |
| |
| #include "big_integer.h" |
| |
| #include <cassert> |
| #include <cctype> |
| #include <cstdint> |
| #include <iostream> |
| #include <sstream> |
| |
| namespace ignite { |
| |
| /** |
| * Big decimal number implementation. |
| * |
| * TODO: Modernize this code to C++17 and update coding style |
| */ |
| class big_decimal { |
| public: |
| // Default |
| big_decimal() = default; |
| |
| /** |
| * 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. |
| */ |
| big_decimal(const std::int8_t *mag, std::int32_t len, std::int16_t scale, std::int8_t sign, bool big_endian = true) |
| : m_scale(std::int16_t(scale & 0x7FFF)) |
| , m_magnitude(mag, len, sign, big_endian) {} |
| |
| /** |
| * Constructs a big decimal from the byte array. |
| * |
| * @param data Bytes of the decimal. Scale in little byte order, magnitude as a @ref big_integer. |
| * @param size The number of bytes. |
| */ |
| big_decimal(const std::byte *data, std::size_t size); |
| |
| /** |
| * Integer constructor. |
| * |
| * @param val Integer value. |
| */ |
| explicit big_decimal(int64_t val) |
| : m_magnitude(val) {} |
| |
| /** |
| * Integer constructor with scale. |
| * |
| * @param val Integer value. |
| * @param scale Scale. |
| */ |
| big_decimal(int64_t val, int16_t scale) |
| : m_scale(scale) |
| , m_magnitude(val) {} |
| |
| /** |
| * big_integer constructor with scale. |
| * |
| * @param val big_integer value. |
| * @param scale Scale. |
| */ |
| big_decimal(const big_integer &val, int16_t scale) |
| : m_scale(scale) |
| , m_magnitude(val) {} |
| |
| /** |
| * big_integer constructor with scale. |
| * |
| * @param val big_integer value. |
| * @param scale Scale. |
| */ |
| big_decimal(big_integer &&val, int16_t scale) |
| : m_scale(scale) |
| , m_magnitude(std::forward<big_integer>(val)) {} |
| |
| /** |
| * String constructor. |
| * |
| * @param val String to assign. |
| * @param len String length. |
| */ |
| explicit big_decimal(const char *val, int32_t len) |
| : m_magnitude(0) { |
| assign_string(val, len); |
| } |
| |
| /** |
| * String constructor. |
| * |
| * @param val String to assign. |
| */ |
| explicit big_decimal(const std::string &val) |
| : m_magnitude(0) { |
| assign_string(val); |
| } |
| |
| /** |
| * From double. |
| * |
| * @param val Double value. |
| * @return An instance of big_decimal from double. |
| */ |
| static big_decimal from_double(double val) { |
| big_decimal res; |
| res.assign_double(val); |
| return res; |
| } |
| |
| /** |
| * Get number of bytes required to store this decimal as byte array. |
| * |
| * @return Number of bytes required to store this decimal as byte array. |
| */ |
| [[nodiscard]] std::size_t byte_size() const noexcept; |
| |
| /** |
| * Store this decimal as a byte array. |
| * |
| * @param data Destination byte array. Its size must be at least as large as the value returned by @ref |
| * byte_size(); |
| */ |
| void store_bytes(std::byte *data) const; |
| |
| /** |
| * Convert value to bytes. |
| * |
| * @return Vector of bytes. |
| */ |
| [[nodiscard]] std::vector<std::byte> to_bytes() const { |
| std::vector<std::byte> bytes(byte_size()); |
| store_bytes(bytes.data()); |
| return bytes; |
| } |
| |
| /** |
| * Convert to double. |
| */ |
| explicit operator double() const { return to_double(); } |
| |
| /** |
| * Convert to int64_t. |
| */ |
| explicit operator int64_t() const { return to_int64(); } |
| |
| /** |
| * Convert to double. |
| * |
| * @return Double value. |
| */ |
| [[nodiscard]] double to_double() const { |
| std::stringstream stream; |
| stream << *this; |
| |
| double result; |
| stream >> result; |
| return result; |
| } |
| |
| /** |
| * Convert to int64_t. |
| * |
| * @return int64_t value. |
| */ |
| [[nodiscard]] int64_t to_int64() const { |
| if (m_scale == 0) { |
| return m_magnitude.to_int64(); |
| } |
| |
| big_decimal zero_scaled; |
| |
| set_scale(0, zero_scaled); |
| |
| return zero_scaled.m_magnitude.to_int64(); |
| } |
| |
| /** |
| * Get scale. |
| * |
| * @return Scale. |
| */ |
| [[nodiscard]] std::int16_t get_scale() const noexcept { return m_scale; } |
| |
| /** |
| * Set scale. |
| * |
| * @param scale Scale to set. |
| * @param res Result is placed here. Can be *this. |
| */ |
| void set_scale(std::int16_t new_scale, big_decimal &res) const; |
| |
| /** |
| * Get precision of the Decimal. |
| * |
| * @return Number of the decimal digits in the decimal representation |
| * of the value. |
| */ |
| [[nodiscard]] std::int32_t get_precision() const noexcept { return m_magnitude.get_precision(); } |
| |
| /** |
| * Get unscaled value. |
| * |
| * @return Unscaled value. |
| */ |
| [[nodiscard]] const big_integer &get_unscaled_value() const noexcept { return m_magnitude; } |
| |
| /** |
| * Swap function for the Decimal type. |
| * |
| * @param other Other instance. |
| */ |
| friend void swap(big_decimal &lhs, big_decimal &rhs) { |
| using std::swap; |
| |
| swap(lhs.m_scale, rhs.m_scale); |
| swap(lhs.m_magnitude, rhs.m_magnitude); |
| } |
| |
| /** |
| * Assign specified value to this Decimal. |
| * |
| * @param val String to assign. |
| */ |
| void assign_string(const std::string &val) { assign_string(val.data(), static_cast<int32_t>(val.size())); } |
| |
| /** |
| * Assign specified value to this Decimal. |
| * |
| * @param val String to assign. |
| * @param len String length. |
| */ |
| void assign_string(const char *val, int32_t len) { |
| std::stringstream converter; |
| |
| converter.write(val, len); |
| |
| converter >> *this; |
| } |
| |
| /** |
| * Assign specified value to this Decimal. |
| * |
| * @param val Value to assign. |
| */ |
| void assign_int64(int64_t val) { |
| m_magnitude.assign_int64(val); |
| |
| m_scale = 0; |
| } |
| |
| /** |
| * Assign specified value to this Decimal. |
| * |
| * @param val Value to assign. |
| */ |
| void assign_double(double val) { |
| std::stringstream converter; |
| |
| converter.precision(16); |
| |
| converter << val; |
| converter >> *this; |
| } |
| |
| /** |
| * Assign specified value to this Decimal. |
| * |
| * @param val Value to assign. |
| */ |
| void assign_uint64(uint64_t val) { |
| m_magnitude.assign_uint64(val); |
| |
| m_scale = 0; |
| } |
| |
| /** |
| * Add another big decimal to this. |
| * |
| * @param other Addendum. Can be *this. |
| * @param res Result placed there. Can be *this. |
| */ |
| void add(const big_decimal &other, big_decimal &res) const; |
| |
| /** |
| * Subtract another big decimal from this. |
| * |
| * @param other Subtrahend. Can be *this. |
| * @param res Result placed there. Can be *this. |
| */ |
| void subtract(const big_decimal &other, big_decimal &res) const; |
| |
| /** |
| * Muitiply this to another big decimal. |
| * |
| * @param other Another instance. Can be *this. |
| * @param res Result placed there. Can be *this. |
| */ |
| void multiply(const big_decimal &other, big_decimal &res) const; |
| |
| /** |
| * Divide this to another big decimal. |
| * |
| * @param divisor Divisor. Can be *this. |
| * @param res Result placed there. Can be *this. |
| */ |
| void divide(const big_decimal &other, big_decimal &res) const; |
| |
| /** |
| * Reverses sign of this value. |
| */ |
| void negate() { m_magnitude.negate(); } |
| |
| /** |
| * 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. |
| */ |
| [[nodiscard]] int compare(const big_decimal &other) const; |
| |
| /** |
| * Check whether this value is negative. |
| * |
| * @return True if this value is negative and false otherwise. |
| */ |
| [[nodiscard]] bool is_negative() const noexcept { return m_magnitude.is_negative(); } |
| |
| /** |
| * Check whether this value is zero. |
| * |
| * @return True if this value is negative and false otherwise. |
| */ |
| [[nodiscard]] bool is_zero() const noexcept { return m_magnitude.is_zero(); } |
| |
| /** |
| * Check whether this value is positive. |
| * |
| * @return True if this value is positive and false otherwise. |
| */ |
| [[nodiscard]] bool is_positive() const noexcept { return m_magnitude.is_positive(); } |
| |
| /** |
| * 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 big_decimal &val); |
| |
| /** |
| * Input operator. |
| * |
| * @param is Input stream. |
| * @param val Value to input. |
| * @return Reference to the first param. |
| */ |
| friend std::istream &operator>>(std::istream &is, big_decimal &val); |
| |
| private: |
| /** Scale. */ |
| std::int16_t m_scale = 0; |
| |
| /** Magnitude. */ |
| big_integer m_magnitude; |
| }; |
| |
| /** |
| * @brief Comparison operator. |
| * |
| * @param lhs First value. |
| * @param rhs Second value. |
| * @return true If the first value is equal to the second. |
| */ |
| inline bool operator==(const big_decimal &lhs, const big_decimal &rhs) noexcept { |
| return lhs.compare(rhs) == 0; |
| } |
| |
| /** |
| * @brief Comparison operator. |
| * |
| * @param lhs First value. |
| * @param rhs Second value. |
| * @return true If the first value is not equal to the second. |
| */ |
| inline bool operator!=(const big_decimal &lhs, const big_decimal &rhs) noexcept { |
| return lhs.compare(rhs) != 0; |
| } |
| |
| /** |
| * @brief Comparison operator. |
| * |
| * @param lhs First value. |
| * @param rhs Second value. |
| * @return true If the first value is less than the second. |
| */ |
| inline bool operator<(const big_decimal &lhs, const big_decimal &rhs) noexcept { |
| return lhs.compare(rhs) < 0; |
| } |
| |
| /** |
| * @brief Comparison operator. |
| * |
| * @param lhs First value. |
| * @param rhs Second value. |
| * @return true If the first value is less than or equal to the second. |
| */ |
| inline bool operator<=(const big_decimal &lhs, const big_decimal &rhs) noexcept { |
| return lhs.compare(rhs) <= 0; |
| } |
| |
| /** |
| * @brief Comparison operator. |
| * |
| * @param lhs First value. |
| * @param rhs Second value. |
| * @return true If the first value is greater than the second. |
| */ |
| inline bool operator>(const big_decimal &lhs, const big_decimal &rhs) noexcept { |
| return lhs.compare(rhs) > 0; |
| } |
| |
| /** |
| * @brief Comparison operator. |
| * |
| * @param lhs First value. |
| * @param rhs Second value. |
| * @return true If the first value is greater than or equal to the second. |
| */ |
| inline bool operator>=(const big_decimal &lhs, const big_decimal &rhs) noexcept { |
| return lhs.compare(rhs) >= 0; |
| } |
| |
| /** |
| * @brief Sum operator. |
| * |
| * @param lhs First value. |
| * @param rhs Second value. |
| * @return New value that holds result of lhs + rhs. |
| */ |
| inline big_decimal operator+(const big_decimal &lhs, const big_decimal &rhs) noexcept { |
| big_decimal res; |
| lhs.add(rhs, res); |
| return res; |
| } |
| |
| /** |
| * @brief Subtract operator. |
| * |
| * @param lhs First value. |
| * @param rhs Second value. |
| * @return New value that holds result of lhs - rhs. |
| */ |
| inline big_decimal operator-(const big_decimal &lhs, const big_decimal &rhs) noexcept { |
| big_decimal res; |
| lhs.subtract(rhs, res); |
| return res; |
| } |
| |
| /** |
| * @brief Multiply operator. |
| * |
| * @param lhs First value. |
| * @param rhs Second value. |
| * @return New value that holds result of lhs * rhs. |
| */ |
| inline big_decimal operator*(const big_decimal &lhs, const big_decimal &rhs) noexcept { |
| big_decimal res; |
| lhs.multiply(rhs, res); |
| return res; |
| } |
| |
| /** |
| * @brief Divide operator. |
| * |
| * @param lhs First value. |
| * @param rhs Second value. |
| * @return New value that holds result of lhs / rhs. |
| */ |
| inline big_decimal operator/(const big_decimal &lhs, const big_decimal &rhs) noexcept { |
| big_decimal res; |
| lhs.divide(rhs, res); |
| return res; |
| } |
| |
| } // namespace ignite |