]> git.localhorst.tv Git - l2e.git/blobdiff - src/math/Fixed.h
added fixed point number type
[l2e.git] / src / math / Fixed.h
diff --git a/src/math/Fixed.h b/src/math/Fixed.h
new file mode 100644 (file)
index 0000000..a398275
--- /dev/null
@@ -0,0 +1,363 @@
+#ifndef MATH_FIXED_H_
+#define MATH_FIXED_H_
+
+#include "utility.h"
+
+#include <cmath>
+#include <limits>
+#include <ostream>
+#include <SDL.h>
+
+
+namespace math {
+
+/// Fixed point number type.
+/// The first bit is used for the sign (0 = positive, 1 = negative), the last
+/// fracBits bits are used for the fractional part and the remaining middle
+/// bits represent the integer value.
+/// The sign bit of zero is undefined.
+template<Uint8 fracBits>
+class Fixed {
+
+template<Uint8>
+friend class Fixed;
+
+public:
+       /// Create a fixed point decimal representing i/f .
+       Fixed(Sint32 i = 0, Uint32 f = 1)
+       : rep(((i < 0 ? (i * -1) : i) << IntShift()) & IntMask()) {
+               rep /= f;
+               if (i < 0) {
+                       rep |= SignMask();
+               }
+       }
+       explicit Fixed(double d)
+       : rep(0) {
+               rep = std::abs(d) * (1 << IntShift());
+               if (d < 0) {
+                       rep |= SignMask();
+               }
+       }
+       template<Uint8 otherFrac>
+       explicit Fixed(const Fixed<otherFrac> &other)
+       : rep(0) {
+               Uint32 otherInt = other.RawInt();
+               rep = DistanceShift(otherInt, fracBits, otherFrac);
+               if (other.Sigma() < 0) {
+                       rep |= SignMask();
+               }
+       }
+
+public:
+       int Int() const { return IntPart() * Sigma(); }
+       int Sigma() const {
+               if (IntPart() == 0 && FracPart() == 0) {
+                       return 0;
+               } else if (SignPart() == 0) {
+                       return 1;
+               } else {
+                       return -1;
+               }
+       }
+       double Double() const { return double(SignedInt()) / (1 << IntShift()); }
+
+public:
+       bool operator ==(const Fixed &other) const {
+               return rep == other.rep;
+       }
+       bool operator !=(const Fixed &other) const {
+               return rep != other.rep;
+       }
+       bool operator <(const Fixed &other) const {
+               return SignedInt() < other.SignedInt();
+       }
+       bool operator <=(const Fixed &other) const {
+               return SignedInt() <= other.SignedInt();
+       }
+       bool operator >(const Fixed &other) const {
+               return SignedInt() > other.SignedInt();
+       }
+       bool operator >=(const Fixed &other) const {
+               return SignedInt() >= other.SignedInt();
+       }
+
+       Fixed operator -() const {
+               Fixed neg;
+               neg.rep = rep ^ SignMask();
+               return neg;
+       }
+
+       Fixed &operator +=(const Fixed &other) {
+               Sint32 temp = SignedInt() + other.SignedInt();
+               if (temp < 0) {
+                       rep = (temp * -1) | SignMask();
+               } else {
+                       rep = temp;
+               }
+               return *this;
+       }
+       Fixed &operator +=(int i) {
+               Sint32 temp = SignedInt() + (i << IntShift());
+               if (temp < 0) {
+                       rep = (temp * -1) | SignMask();
+               } else {
+                       rep = temp;
+               }
+               return *this;
+       }
+       Fixed &operator -=(const Fixed &other) {
+               Sint32 temp = SignedInt() - other.SignedInt();
+               if (temp < 0) {
+                       rep = (temp * -1) | SignMask();
+               } else {
+                       rep = temp;
+               }
+               return *this;
+       }
+       Fixed &operator -=(int i) {
+               Sint32 temp = SignedInt() - (i << IntShift());
+               if (temp < 0) {
+                       rep = (temp * -1) | SignMask();
+               } else {
+                       rep = temp;
+               }
+               return *this;
+       }
+       Fixed &operator *=(const Fixed &other) {
+               Sint64 temp = SignedInt();
+               temp *= other.SignedInt();
+               temp /= (1 << IntShift());
+               if (temp < 0) {
+                       rep = (temp * -1) | SignMask();
+               } else {
+                       rep = temp;
+               }
+               return *this;
+       }
+       Fixed &operator *=(int i) {
+               Sint32 temp = SignedInt() * i;
+               if (temp < 0) {
+                       rep = (temp * -1) | SignMask();
+               } else {
+                       rep = temp;
+               }
+               return *this;
+       }
+       Fixed &operator /=(const Fixed &other) {
+               Sint64 temp = SignedInt();
+               temp *= (1 << IntShift());
+               temp /= other.SignedInt();
+               if (temp < 0) {
+                       rep = (temp * -1) | SignMask();
+               } else {
+                       rep = temp;
+               }
+               return *this;
+       }
+       Fixed &operator /=(int i) {
+               Sint32 temp = SignedInt() / i;
+               if (temp < 0) {
+                       rep = (temp * -1) | SignMask();
+               } else {
+                       rep = temp;
+               }
+               return *this;
+       }
+       Fixed &operator %=(const Fixed &other) {
+               const Uint32 myRaw = RawInt();
+               const Uint32 otherRaw = other.RawInt();
+               const Uint32 intQuotient = myRaw / otherRaw;
+               rep = (myRaw - (otherRaw  * intQuotient)) | (rep & SignMask());
+               return *this;
+       }
+       Fixed &operator %=(int i) {
+               const Uint32 myRaw = RawInt();
+               const Uint32 otherRaw = (i << IntShift());
+               const Uint32 intQuotient = myRaw / otherRaw;
+               rep = (myRaw - (otherRaw  * intQuotient)) | (rep & SignMask());
+               return *this;
+       }
+
+private:
+       int SignBits() const { return 1; }
+       int IntBits() const { return 32 - SignBits() - FracBits(); }
+       int FracBits() const { return fracBits; }
+
+       int SignShift() const { return 31; }
+       int IntShift() const { return fracBits; }
+       int FracShift() const { return 0; }
+
+       Uint32 SignMask() const { return 1 << SignShift(); }
+       Uint32 IntMask() const { return ~(SignMask() | FracMask()); }
+       Uint32 FracMask() const { return (1 << IntShift()) - 1; }
+
+       int SignPart() const { return (rep & SignMask()) >> SignShift(); }
+       int IntPart() const { return (rep & IntMask()) >> IntShift(); }
+       int FracPart() const { return (rep & FracMask()) >> FracShift(); }
+
+       Sint32 SignedInt() const { return Sigma() * RawInt(); }
+       Uint32 RawInt() const { return rep & (~SignMask()); }
+
+private:
+       Uint32 rep;
+
+};
+
+
+template<Uint8 T, Uint8 U>
+inline bool operator ==(const Fixed<T> &lhs, const Fixed<U> &rhs) {
+       if (T < U) {
+               return lhs == Fixed<T>(rhs);
+       } else {
+               return Fixed<U>(lhs) == rhs;
+       }
+}
+
+template<Uint8 T, Uint8 U>
+inline bool operator !=(const Fixed<T> &lhs, const Fixed<U> &rhs) {
+       return !(lhs == rhs);
+}
+
+template<Uint8 T, Uint8 U>
+inline bool operator <(const Fixed<T> &lhs, const Fixed<U> &rhs) {
+       if (T < U) {
+               return lhs < Fixed<T>(rhs);
+       } else {
+               return Fixed<U>(lhs) < rhs;
+       }
+}
+
+template<Uint8 T, Uint8 U>
+inline bool operator <=(const Fixed<T> &lhs, const Fixed<U> &rhs) {
+       if (T < U) {
+               return lhs <= Fixed<T>(rhs);
+       } else {
+               return Fixed<U>(lhs) <= rhs;
+       }
+}
+
+template<Uint8 T, Uint8 U>
+inline bool operator >(const Fixed<T> &lhs, const Fixed<U> &rhs) {
+       return !(lhs <= rhs);
+}
+
+template<Uint8 T, Uint8 U>
+inline bool operator >=(const Fixed<T> &lhs, const Fixed<U> &rhs) {
+       return !(lhs < rhs);
+}
+
+template<Uint8 T, Uint8 U>
+inline Fixed<T> operator +(const Fixed<T> &lhs, const Fixed<U> &rhs) {
+       Fixed<T> temp(lhs);
+       return temp += rhs;
+}
+template<Uint8 T, class Other>
+inline Fixed<T> operator +(const Fixed<T> &lhs, const Other &rhs) {
+       Fixed<T> temp(lhs);
+       return temp += rhs;
+}
+template<Uint8 T, class Other>
+inline Fixed<T> operator +(const Other &lhs, const Fixed<T> &rhs) {
+       return rhs + lhs;
+}
+
+template<Uint8 T, Uint8 U>
+inline Fixed<T> operator -(const Fixed<T> &lhs, const Fixed<U> &rhs) {
+       Fixed<T> temp(lhs);
+       return temp -= rhs;
+}
+template<Uint8 T, class Other>
+inline Fixed<T> operator -(const Fixed<T> &lhs, const Other &rhs) {
+       Fixed<T> temp(lhs);
+       return temp -= rhs;
+}
+template<Uint8 T, class Other>
+inline Fixed<T> operator -(const Other &lhs, const Fixed<T> &rhs) {
+       return lhs + -rhs;
+}
+
+template<Uint8 T, Uint8 U>
+inline Fixed<T> operator *(const Fixed<T> &lhs, const Fixed<U> &rhs) {
+       Fixed<T> temp(lhs);
+       return temp *= rhs;
+}
+template<Uint8 T, class Other>
+inline Fixed<T> operator *(const Fixed<T> &lhs, const Other &rhs) {
+       Fixed<T> temp(lhs);
+       return temp *= rhs;
+}
+template<Uint8 T, class Other>
+inline Fixed<T> operator *(const Other &lhs, const Fixed<T> &rhs) {
+       return rhs * lhs;
+}
+
+template<Uint8 T, Uint8 U>
+inline Fixed<T> operator /(const Fixed<T> &lhs, const Fixed<U> &rhs) {
+       Fixed<T> temp(lhs);
+       return temp /= rhs;
+}
+template<Uint8 T, class Other>
+inline Fixed<T> operator /(const Fixed<T> &lhs, const Other &rhs) {
+       Fixed<T> temp(lhs);
+       return temp /= rhs;
+}
+
+template<Uint8 T, Uint8 U>
+inline Fixed<T> operator %(const Fixed<T> &lhs, const Fixed<U> &rhs) {
+       Fixed<T> temp(lhs);
+       return temp %= rhs;
+}
+template<Uint8 T, class Other>
+inline Fixed<T> operator %(const Fixed<T> &lhs, const Other &rhs) {
+       Fixed<T> temp(lhs);
+       return temp %= rhs;
+}
+
+
+template<Uint8 T>
+inline std::ostream &operator <<(std::ostream &out, const Fixed<T> &f) {
+       return out << f.Double();
+}
+
+}
+
+
+namespace std {
+
+template <Uint8 fracBits>
+class numeric_limits<math::Fixed<fracBits> > {
+
+public:
+       static const bool is_specialized = true;
+       static math::Fixed<fracBits> min() throw() {
+               return max() * -1;
+       }
+       static math::Fixed<fracBits> max() throw() {
+               return math::Fixed<fracBits>(1 << (digits - fracBits) - 1)
+                               + math::Fixed<fracBits>((1 << fracBits) - 2, (1 << fracBits) - 1);
+       }
+       static const int digits = 31;
+       static const int digits10 = 10;
+       static const bool is_signed = true;
+       static const bool is_integer = false;
+       static const bool is_exact = true;
+       static const int radix = 2;
+       static math::Fixed<fracBits> epsilon() throw() {
+               return math::Fixed<fracBits>(1, 1 << (digits - fracBits) - 1);
+       }
+       static math::Fixed<fracBits> round_error() throw() {
+               return epsilon();
+       }
+
+       static const bool is_iec559 = false;
+       static const bool is_bounded = true;
+       static const bool is_modulo = false;
+
+       static const bool traps = false;
+       static const bool tinyness_before = false;
+       static const float_round_style round_style = round_toward_zero;
+};
+
+}
+
+#endif