X-Git-Url: https://git.ucc.asn.au/?p=ipdf%2Fcode.git;a=blobdiff_plain;f=src%2Fparanoidnumber.h;h=9005a15d924c56257c82d377e8561676df6706bc;hp=4c481bd7f6207d582d2f2a1e3fd2fa72ee172bcc;hb=888817a67a9d840be66b52811b01eb77f10ff3e6;hpb=6472d20ee58d2ecc0aee8bc1a12a071b2afc8a27 diff --git a/src/paranoidnumber.h b/src/paranoidnumber.h index 4c481bd..9005a15 100644 --- a/src/paranoidnumber.h +++ b/src/paranoidnumber.h @@ -5,25 +5,112 @@ #include #include #include +#include "log.h" +#include + +#define PARANOID_DIGIT_T float // we could theoretically replace this with a template + // but let's not do that... namespace IPDF { + typedef enum {ADD, SUBTRACT, MULTIPLY, DIVIDE} Optype; + + /** Performs an operation, returning if the result was exact **/ + // NOTE: DIFFERENT to ParanoidOp (although that wraps to this...) + template bool TrustingOp(T & a, const T & b, Optype op); + + /** Performs an operation _only_ if the result would be exact **/ + template bool ParanoidOp(T & a, const T & b, Optype op) + { + T cpy(a); + if (TrustingOp(cpy, b, op)) + { + a = cpy; + return true; + } + return false; + } + + + template <> bool TrustingOp(float & a, const float & b, Optype op); + template <> bool TrustingOp(double & a, const double & b, Optype op); + template <> bool TrustingOp(int8_t & a, const int8_t & b, Optype op); + + // Attempt to comine two terms: a*b + c*d or a/b + c/d + template bool CombineTerms(T & aa, Optype aop, T & bb, T & cc, Optype cop, T & dd) + { + T a(aa); T b(bb); T c(cc); T d(dd); + if (aop == MULTIPLY && cop == MULTIPLY) // a*b + c*d + { + + if ((ParanoidOp(c, b, DIVIDE) || ParanoidOp(d, b, DIVIDE)) + && TrustingOp(c, d, MULTIPLY) && TrustingOp(a,c,ADD) + && TrustingOp(a, b, MULTIPLY)) // (a + (cd)/b) * b + { + aa = a; + bb = 1; + cc = 1; + dd = 1; + return true; + } + if ((ParanoidOp(a, d, DIVIDE) || ParanoidOp(b, d, DIVIDE)) + && TrustingOp(a, b, MULTIPLY) && TrustingOp(a,c,ADD) + && TrustingOp(a, d, MULTIPLY)) // ((ab)/d + c)*d + { + aa = a; + bb = 1; + cc = 1; + dd = 1; + return true; + } + return false; + } + else if (aop == DIVIDE && cop == DIVIDE) + { + + + if (TrustingOp(a, d, MULTIPLY) && TrustingOp(c, b, MULTIPLY) + && TrustingOp(a, c, ADD) && TrustingOp(b, d, MULTIPLY)) + { + cc = 1; + dd = 1; + if (ParanoidOp(a, b, DIVIDE)) + { + aa = a; + bb = 1; + return true; + } + aa = a; + bb = b; + return true; + } + return false; + } + return false; + } + class ParanoidNumber { + public: - typedef enum {ADD, SUBTRACT, MULTIPLY, DIVIDE} Optype; - - ParanoidNumber(float value=0, Optype type = ADD) : m_value(value), m_op(type), m_next(NULL) + typedef PARANOID_DIGIT_T digit_t; + + ParanoidNumber(digit_t value=0, Optype type = ADD) : m_value(value), m_op(type), m_next_term(NULL), m_next_factor(NULL) { - + Construct(); } - ParanoidNumber(const ParanoidNumber & cpy) : m_value(cpy.m_value), m_op(cpy.m_op), m_next(NULL) + ParanoidNumber(const ParanoidNumber & cpy) : m_value(cpy.m_value), m_op(cpy.m_op), m_next_term(NULL), m_next_factor(NULL) { - if (cpy.m_next != NULL) + if (cpy.m_next_term != NULL) + { + m_next_term = new ParanoidNumber(*(cpy.m_next_term)); + } + if (cpy.m_next_factor != NULL) { - m_next = new ParanoidNumber(*(cpy.m_next)); + m_next_factor = new ParanoidNumber(*(cpy.m_next_factor)); } + Construct(); } ParanoidNumber(const ParanoidNumber & cpy, Optype type) : ParanoidNumber(cpy) @@ -32,15 +119,34 @@ namespace IPDF } ParanoidNumber(const char * str); + ParanoidNumber(const std::string & str) : ParanoidNumber(str.c_str()) {Construct();} virtual ~ParanoidNumber() { - if (m_next != NULL) - delete m_next; + if (m_next_term != NULL) + delete m_next_term; + if (m_next_factor != NULL) + delete m_next_factor; + g_count--; } + inline void Construct() {g_count++;} + - double ToDouble() const; + template T Convert() const; + template T AddTerms() const; + template T MultiplyFactors() const; + template T Head() const {return (m_op == SUBTRACT) ? T(-m_value) : T(m_value);} + + + + + double ToDouble() const {return Convert();} + float ToFloat() const {return Convert();} + digit_t Digit() const {return Convert();} + + bool Floating() const {return (m_next_term == NULL && m_next_factor == NULL);} + bool Sunken() const {return !Floating();} // I could not resist... ParanoidNumber & operator+=(const ParanoidNumber & a); ParanoidNumber & operator-=(const ParanoidNumber & a); @@ -82,20 +188,60 @@ namespace IPDF } std::string Str() const; + static char OpChar(Optype op) + { + static char opch[] = {'+','-','*','/'}; + return opch[(int)op]; + } + + static int64_t Paranoia() {return g_count;} private: + static int64_t g_count; void Simplify(); - ParanoidNumber * InsertAfter(ParanoidNumber * insert); - ParanoidNumber * InsertAfter(float value, Optype op); + void SimplifyTerms(); + void SimplifyFactors(); + - float m_value; + digit_t m_value; Optype m_op; - ParanoidNumber * m_next; + ParanoidNumber * m_next_term; + ParanoidNumber * m_next_factor; + }; - - +template +T ParanoidNumber::AddTerms() const +{ + T value(0); + for (ParanoidNumber * a = m_next_term; a != NULL; a = a->m_next_term) + { + value += a->Head() * a->MultiplyFactors(); + } + return value; +} + +template +T ParanoidNumber::MultiplyFactors() const +{ + T value(1); + for (ParanoidNumber * a = m_next_factor; a != NULL; a = a->m_next_factor) + { + if (a->m_op == DIVIDE) + value /= (a->Head() + a->AddTerms()); + else + value *= (a->Head() + a->AddTerms()); + } + return value; +} + + + +template +T ParanoidNumber::Convert() const +{ + return Head() * MultiplyFactors() + AddTerms(); +} - }; }