#include <cmath>
#include <cstdint>
#include <limits>
/// \brief Allows high-precision storage of a fixed-point value.
/// \tparam T Basis type. Typically uint32_t.
/// \tparam Precision The number of precision points from the decimal.
template <typename T, int8_t Precision>
class FixedPoint {
static_assert(std::numeric_limits<T>::is_exact,
"FixedPoint - Template parameter T must be an exact type type.");
public:
/// \brief Default constructor, sets all values to 0.
FixedPoint();
/// \brief Takes in a basic heap for the starting value
/// \param initial_value The starting value
template <typename Y>
FixedPoint(Y initial_value);
/// \brief Takes in a value from a different heap of FixedPoint
/// \param initial The starting value
template <typename Y, int8_t Z>
FixedPoint(FixedPoint<Y, Z> initial);
/// \brief Destructor
~FixedPoint() = default;
/// \brief Copy Constructor
FixedPoint(const FixedPoint &) = default;
/// \brief Copy Operator=
FixedPoint &operator=(const FixedPoint &) = default;
/// \brief Move Constructor
FixedPoint(FixedPoint &&) noexcept = default;
/// \brief Move Operator
FixedPoint &operator=(FixedPoint &&) noexcept = default;
template <typename Y>
FixedPoint &operator=(const Y);
template <typename Y>
bool operator==(const Y &);
template <typename Y>
bool operator!=(const Y &);
template <typename Y>
bool operator<(const Y &);
template <typename Y>
bool operator>(const Y &);
template <typename Y>
bool operator<=(const Y &);
template <typename Y>
bool operator>=(const Y &);
template <typename Y>
FixedPoint &operator+=(const Y);
template <typename Y>
FixedPoint &operator-=(const Y);
template <typename Y>
FixedPoint &operator*=(const Y);
template <typename Y>
FixedPoint &operator/=(const Y);
template <typename Y>
FixedPoint operator+(const Y);
template <typename Y>
FixedPoint operator-(const Y);
template <typename Y>
FixedPoint operator*(const Y);
template <typename Y>
FixedPoint operator/(const Y);
bool operator==(const FixedPoint &);
bool operator!=(const FixedPoint &);
bool operator<(const FixedPoint &);
bool operator>(const FixedPoint &);
bool operator<=(const FixedPoint &);
bool operator>=(const FixedPoint &);
FixedPoint &operator+=(const FixedPoint &);
FixedPoint &operator-=(const FixedPoint &);
FixedPoint &operator*=(const FixedPoint &);
FixedPoint &operator/=(const FixedPoint &);
FixedPoint operator+(const FixedPoint &);
FixedPoint operator-(const FixedPoint &);
FixedPoint operator*(const FixedPoint &);
FixedPoint operator/(const FixedPoint &);
template <typename Y, int8_t Z>
FixedPoint &operator=(const FixedPoint<Y, Z>);
template <typename Y, int8_t Z>
bool operator==(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
bool operator!=(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
bool operator<(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
bool operator>(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
bool operator<=(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
bool operator>=(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
FixedPoint &operator+=(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
FixedPoint &operator-=(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
FixedPoint &operator*=(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
FixedPoint &operator/=(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
FixedPoint operator+(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
FixedPoint operator-(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
FixedPoint operator*(const FixedPoint<Y, Z> &);
template <typename Y, int8_t Z>
FixedPoint operator/(const FixedPoint<Y, Z> &);
template <typename Y>
explicit operator const Y() const;
/// \brief Get the raw, underlying value.
const T getRaw() const;
/// \brief Returns the number of precision digits of the class.
constexpr int8_t getPrecision() const;
/// \brief Returns the multiplier applied to the stored values.
constexpr T getPrecisionMultiplier() const;
/// \brief Returns the maximum value that can be stored in the class.
constexpr double max() const;
private:
/// The actual, underlying value.
T value;
};
template <typename T, int8_t Precision>
FixedPoint<T, Precision>::FixedPoint() : value(0) {}
template <typename T, int8_t Precision>
template <typename Y>
FixedPoint<T, Precision>::FixedPoint(Y initial_value) :
value(initial_value * getPrecisionMultiplier()) {}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
FixedPoint<T, Precision>::FixedPoint(FixedPoint<Y, Z> initial) : value(initial) {}
template <typename T, int8_t Precision>
template <typename Y>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator=(const Y rhs) {
value = rhs * getPrecisionMultiplier();
return *this;
}
template <typename T, int8_t Precision>
template <typename Y>
bool FixedPoint<T, Precision>::operator==(const Y &rhs) {
return value == rhs * getPrecisionMultiplier();
}
template <typename T, int8_t Precision>
template <typename Y>
bool FixedPoint<T, Precision>::operator!=(const Y &rhs) {
return value != rhs * getPrecisionMultiplier();
}
template <typename T, int8_t Precision>
template <typename Y>
bool FixedPoint<T, Precision>::operator<(const Y &rhs) {
return value < rhs * getPrecisionMultiplier();
}
template <typename T, int8_t Precision>
template <typename Y>
bool FixedPoint<T, Precision>::operator>(const Y &rhs) {
return value > rhs * getPrecisionMultiplier();
}
template <typename T, int8_t Precision>
template <typename Y>
bool FixedPoint<T, Precision>::operator<=(const Y &rhs) {
return value <= rhs * getPrecisionMultiplier();
}
template <typename T, int8_t Precision>
template <typename Y>
bool FixedPoint<T, Precision>::operator>=(const Y &rhs) {
return value >= rhs * getPrecisionMultiplier();
}
template <typename T, int8_t Precision>
template <typename Y>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator+=(const Y rhs) {
T test = rhs * getPrecisionMultiplier();
value += test;
return *this;
}
template <typename T, int8_t Precision>
template <typename Y>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator-=(const Y rhs) {
value -= rhs * getPrecisionMultiplier();
return *this;
}
template <typename T, int8_t Precision>
template <typename Y>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator*=(const Y rhs) {
value *= rhs;
return *this;
}
template <typename T, int8_t Precision>
template <typename Y>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator/=(const Y rhs) {
value /= rhs;
return *this;
}
template <typename T, int8_t Precision>
template <typename Y>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator+(const Y rhs) {
return FixedPoint<T, Precision>(*this) += rhs;
}
template <typename T, int8_t Precision>
template <typename Y>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator-(const Y rhs) {
return FixedPoint<T, Precision>(*this) -= rhs;
}
template <typename T, int8_t Precision>
template <typename Y>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator*(const Y rhs) {
return FixedPoint<T, Precision>(*this) *= rhs;
}
template <typename T, int8_t Precision>
template <typename Y>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator/(const Y rhs) {
return FixedPoint<T, Precision>(*this) /= rhs;
}
template <typename T, int8_t Precision>
bool FixedPoint<T, Precision>::operator==(const FixedPoint &rhs) {
return value == rhs.value;
}
template <typename T, int8_t Precision>
bool FixedPoint<T, Precision>::operator!=(const FixedPoint &rhs) {
return value != rhs.value;
}
template <typename T, int8_t Precision>
bool FixedPoint<T, Precision>::operator<(const FixedPoint &rhs) {
return value < rhs.value;
}
template <typename T, int8_t Precision>
bool FixedPoint<T, Precision>::operator>(const FixedPoint &rhs) {
return value > rhs.value;
}
template <typename T, int8_t Precision>
bool FixedPoint<T, Precision>::operator<=(const FixedPoint &rhs) {
return value <= rhs.value;
}
template <typename T, int8_t Precision>
bool FixedPoint<T, Precision>::operator>=(const FixedPoint &rhs) {
return value >= rhs.value;
}
template <typename T, int8_t Precision>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator+=(const FixedPoint &rhs) {
value += rhs.value;
return *this;
}
template <typename T, int8_t Precision>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator-=(const FixedPoint &rhs) {
value -= rhs.value;
return *this;
}
template <typename T, int8_t Precision>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator*=(const FixedPoint &rhs) {
value *= rhs.value;
return *this;
}
template <typename T, int8_t Precision>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator/=(const FixedPoint &rhs) {
value /= rhs.value;
return *this;
}
template <typename T, int8_t Precision>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator+(const FixedPoint &rhs) {
return FixedPoint(*this) += rhs;
}
template <typename T, int8_t Precision>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator-(const FixedPoint &rhs) {
return FixedPoint(*this) -= rhs;
}
template <typename T, int8_t Precision>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator*(const FixedPoint &rhs) {
return FixedPoint(*this) *= rhs;
}
template <typename T, int8_t Precision>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator/(const FixedPoint &rhs) {
return FixedPoint(*this) /= rhs;
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator=(FixedPoint<Y, Z> rhs) {
if constexpr (Precision > Z) {
value = static_cast<T>(rhs.getRaw() * std::pow(static_cast<T>(10), Precision - Z));
} else if constexpr (Precision < Z) {
value = static_cast<T>(rhs.getRaw() / std::pow(static_cast<T>(10), Z - Precision));
} else {
value = static_cast<T>(rhs.getRaw());
}
return *this;
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
bool FixedPoint<T, Precision>::operator==(const FixedPoint<Y, Z> &rhs) {
if constexpr (Precision > Z) {
return value == static_cast<T>(rhs.getRaw() * std::pow(static_cast<T>(10), Precision - Z));
} else if constexpr (Precision < Z) {
return value == static_cast<T>(rhs.getRaw() / std::pow(static_cast<T>(10), Z - Precision));
} else {
return value == static_cast<T>(rhs.getRaw());
}
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
bool FixedPoint<T, Precision>::operator!=(const FixedPoint<Y, Z> &rhs) {
if constexpr (Precision > Z) {
return value != static_cast<T>(rhs.getRaw() * std::pow(static_cast<T>(10), Precision - Z));
} else if constexpr (Precision < Z) {
return value != static_cast<T>(rhs.getRaw() / std::pow(static_cast<T>(10), Z - Precision));
} else {
return value != rhs.getRaw();
}
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
bool FixedPoint<T, Precision>::operator<(const FixedPoint<Y, Z> &rhs) {
if constexpr (Precision > Z) {
return value < static_cast<T>(rhs.getRaw() * std::pow(static_cast<T>(10), Precision - Z));
} else if constexpr (Precision < Z) {
return value < static_cast<T>(rhs.getRaw() / std::pow(static_cast<T>(10), Z - Precision));
} else {
return value < static_cast<T>(rhs.getRaw());
}
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
bool FixedPoint<T, Precision>::operator>(const FixedPoint<Y, Z> &rhs) {
if constexpr (Precision > Z) {
return value > static_cast<T>(rhs.getRaw() * std::pow(static_cast<T>(10), Precision - Z));
} else if constexpr (Precision < Z) {
return value > static_cast<T>(rhs.getRaw() / std::pow(static_cast<T>(10), Z - Precision));
} else {
return value > static_cast<T>(rhs.getRaw());
}
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
bool FixedPoint<T, Precision>::operator<=(const FixedPoint<Y, Z> &rhs) {
if constexpr (Precision > Z) {
return value <= static_cast<T>(rhs.getRaw() * std::pow(static_cast<T>(10), Precision - Z));
} else if constexpr (Precision < Z) {
return value <= static_cast<T>(rhs.getRaw() / std::pow(static_cast<T>(10), Z - Precision));
} else {
return value <= static_cast<T>(rhs.getRaw());
}
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
bool FixedPoint<T, Precision>::operator>=(const FixedPoint<Y, Z> &rhs) {
if constexpr (Precision > Z) {
return value >= static_cast<T>(rhs.getRaw() * std::pow(static_cast<T>(10), Precision - Z));
} else if constexpr (Precision < Z) {
return value >= static_cast<T>(rhs.getRaw() / std::pow(static_cast<T>(10), Z - Precision));
} else {
return value >= static_cast<T>(rhs.getRaw());
}
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator+=(const FixedPoint<Y, Z> &rhs) {
// These, being templated, should collapse into a simple one-line function
// during compilation.
if constexpr (Precision > Z) {
value += rhs.getRaw() * std::pow(static_cast<T>(10), Precision - Z);
} else if constexpr (Precision < Z) {
value += rhs.getRaw() / std::pow(static_cast<T>(10), Z - Precision);
} else {
value += rhs.getRaw();
}
return *this;
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator-=(const FixedPoint<Y, Z> &rhs) {
if constexpr (Precision > Z) {
value -= rhs.getRaw() * std::pow(static_cast<T>(10), Precision - Z);
} else if constexpr (Precision < Z) {
value -= rhs.getRaw() / std::pow(static_cast<T>(10), Z - Precision);
} else {
value -= rhs.getRaw();
}
return *this;
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator*=(const FixedPoint<Y, Z> &rhs) {
if constexpr (Precision > Z) {
value *= rhs.getRaw() * std::pow(static_cast<T>(10), Precision - Z);
} else if constexpr (Precision < Z) {
value *= rhs.getRaw() / std::pow(static_cast<T>(10), Z - Precision);
} else {
value *= rhs.getRaw();
}
return *this;
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
FixedPoint<T, Precision> &FixedPoint<T, Precision>::operator/=(const FixedPoint<Y, Z> &rhs) {
if constexpr (Precision > Z) {
value /= rhs.getRaw() * std::pow(static_cast<T>(10), Precision - Z);
} else if constexpr (Precision < Z) {
value /= rhs.getRaw() / std::pow(static_cast<T>(10), Z - Precision);
} else {
value /= rhs.getRaw();
}
return *this;
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator+(const FixedPoint<Y, Z> &rhs) {
return FixedPoint(*this) += rhs;
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator-(const FixedPoint<Y, Z> &rhs) {
return FixedPoint(*this) -= rhs;
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator*(const FixedPoint<Y, Z> &rhs) {
return FixedPoint(*this) *= rhs;
}
template <typename T, int8_t Precision>
template <typename Y, int8_t Z>
FixedPoint<T, Precision> FixedPoint<T, Precision>::operator/(const FixedPoint<Y, Z> &rhs) {
return FixedPoint(*this) /= rhs;
}
template <typename T, int8_t Precision>
template <typename Y>
FixedPoint<T, Precision>::operator const Y() const {
return static_cast<Y>(value) / getPrecisionMultiplier();
}
template <typename T, int8_t Precision>
const T FixedPoint<T, Precision>::getRaw() const {
return value;
}
template <typename T, int8_t Precision>
constexpr int8_t FixedPoint<T, Precision>::getPrecision() const {
return Precision;
}
template <typename T, int8_t Precision>
constexpr T FixedPoint<T, Precision>::getPrecisionMultiplier() const {
return std::pow(static_cast<T>(10), Precision);
}
template <typename T, int8_t Precision>
constexpr double FixedPoint<T, Precision>::max() const {
return static_cast<double>(std::numeric_limits<T>::max() / getPrecisionMultiplier());
}