blob: a6ff99ecd55114a51d1016c20acdaf432b4ad0d8 [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.
*/
#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