--- /dev/null
+#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
--- /dev/null
+#include "FixedTest.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_math::FixedTest);
+
+typedef math::Fixed<16> Fixed;
+typedef math::Fixed<8> LowerFixed;
+typedef math::Fixed<24> HigherFixed;
+
+
+namespace test_math {
+
+void FixedTest::setUp() {
+
+}
+
+void FixedTest::tearDown() {
+
+}
+
+
+void FixedTest::testConversion() {
+ CPPUNIT_ASSERT_EQUAL(1, Fixed(3, 2).Int());
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(0.75, Fixed(3, 4).Double(), std::numeric_limits<double>::epsilon());
+
+ CPPUNIT_ASSERT_EQUAL(-1, Fixed(-3, 2).Int());
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.25, Fixed(-5, 4).Double(), std::numeric_limits<double>::epsilon());
+
+ CPPUNIT_ASSERT_EQUAL(Fixed(3, 2), Fixed(1.5));
+ CPPUNIT_ASSERT_EQUAL(Fixed(-4, 10), Fixed(-0.4));
+
+ CPPUNIT_ASSERT_EQUAL(Fixed(3, 2), Fixed(HigherFixed(3, 2)));
+ CPPUNIT_ASSERT_EQUAL(Fixed(3, 2), Fixed(LowerFixed(3, 2)));
+}
+
+void FixedTest::testComparison() {
+ CPPUNIT_ASSERT_EQUAL(Fixed(), Fixed());
+ CPPUNIT_ASSERT_EQUAL(Fixed(1), Fixed(1));
+ CPPUNIT_ASSERT_EQUAL(Fixed(-1), Fixed(-1));
+
+ CPPUNIT_ASSERT_EQUAL(Fixed(1, 2), Fixed(2, 4));
+
+ CPPUNIT_ASSERT(Fixed(0) != Fixed(1));
+ CPPUNIT_ASSERT(Fixed(0) != Fixed(-1));
+ CPPUNIT_ASSERT(Fixed(1, 2) != Fixed(-1, 2));
+
+ CPPUNIT_ASSERT(Fixed(0) < Fixed(1));
+ CPPUNIT_ASSERT(Fixed(1, 2) <= Fixed(1));
+ CPPUNIT_ASSERT(Fixed(0) > Fixed(-1));
+ CPPUNIT_ASSERT(Fixed(1) >= Fixed(-1, 2));
+
+ CPPUNIT_ASSERT(Fixed() == LowerFixed());
+ CPPUNIT_ASSERT(Fixed() == HigherFixed());
+ CPPUNIT_ASSERT(Fixed(1) == LowerFixed(1));
+ CPPUNIT_ASSERT(Fixed(1) == HigherFixed(1));
+ CPPUNIT_ASSERT(Fixed(-1) == LowerFixed(-1));
+ CPPUNIT_ASSERT(Fixed(-1) == HigherFixed(-1));
+
+ CPPUNIT_ASSERT(Fixed(1, 2) == LowerFixed(2, 4));
+ CPPUNIT_ASSERT(Fixed(1, 2) == HigherFixed(2, 4));
+
+ CPPUNIT_ASSERT(Fixed(0) != LowerFixed(1));
+ CPPUNIT_ASSERT(Fixed(0) != HigherFixed(1));
+ CPPUNIT_ASSERT(Fixed(0) != LowerFixed(-1));
+ CPPUNIT_ASSERT(Fixed(0) != HigherFixed(-1));
+ CPPUNIT_ASSERT(Fixed(1, 2) != LowerFixed(-1, 2));
+ CPPUNIT_ASSERT(Fixed(1, 2) != HigherFixed(-1, 2));
+
+ CPPUNIT_ASSERT(Fixed(0) < LowerFixed(1));
+ CPPUNIT_ASSERT(Fixed(0) < HigherFixed(1));
+ CPPUNIT_ASSERT(Fixed(1, 2) <= LowerFixed(1));
+ CPPUNIT_ASSERT(Fixed(1, 2) <= HigherFixed(1));
+ CPPUNIT_ASSERT(Fixed(0) > LowerFixed(-1));
+ CPPUNIT_ASSERT(Fixed(0) > HigherFixed(-1));
+ CPPUNIT_ASSERT(Fixed(1) >= LowerFixed(-1, 2));
+ CPPUNIT_ASSERT(Fixed(1) >= HigherFixed(-1, 2));
+}
+
+void FixedTest::testSum() {
+ CPPUNIT_ASSERT_EQUAL(
+ Fixed(3, 4),
+ Fixed(1, 4) + Fixed(1, 2));
+ CPPUNIT_ASSERT_EQUAL(
+ Fixed(6, 4),
+ Fixed(2) - Fixed(0.5));
+}
+
+void FixedTest::testProduct() {
+ CPPUNIT_ASSERT_EQUAL(
+ Fixed(5),
+ Fixed(2) * Fixed(2.5));
+ CPPUNIT_ASSERT_EQUAL(
+ Fixed(-1.7499999999),
+ Fixed(3, 4) * Fixed(-7, 3));
+ CPPUNIT_ASSERT_EQUAL(
+ Fixed(4),
+ Fixed(2) / Fixed(0.5));
+ CPPUNIT_ASSERT_EQUAL(
+ Fixed(3),
+ Fixed(15) / Fixed(5));
+}
+
+void FixedTest::testModulo() {
+ CPPUNIT_ASSERT_EQUAL(
+ Fixed(5),
+ Fixed(5) % 6);
+ CPPUNIT_ASSERT_EQUAL(
+ Fixed(5),
+ Fixed(11) % Fixed(6));
+ CPPUNIT_ASSERT_EQUAL(
+ Fixed(2.5),
+ Fixed(7.5) % 5);
+ CPPUNIT_ASSERT_EQUAL(
+ Fixed(0.5),
+ Fixed(8) % Fixed(7.5));
+}
+
+}