From: Sam Moore Date: Wed, 24 Sep 2014 08:54:33 +0000 (+0800) Subject: Allow for negative Paranoid Numbers X-Git-Url: https://git.ucc.asn.au/?p=ipdf%2Fcode.git;a=commitdiff_plain;h=71df61ab8ea302247ad35ccdc973bc8e0cafd5b1 Allow for negative Paranoid Numbers Specifically, when constructed off a string I wasn't checking for a leading '-' sign. That's kind of important. Also 'e' notations will probably have to be included at some point. I am really bad at this... --- diff --git a/src/paranoidnumber.cpp b/src/paranoidnumber.cpp index b2a66f4..40071e1 100644 --- a/src/paranoidnumber.cpp +++ b/src/paranoidnumber.cpp @@ -37,6 +37,12 @@ ParanoidNumber::ParanoidNumber(const string & str) : m_value(0), m_next() int dp = 0; int end = 0; + bool negate = str[0] == '-'; + if (negate) + { + dp++; + end++; + } while (str[dp] != '\0' && str[dp] != '.') { ++dp; @@ -45,7 +51,7 @@ ParanoidNumber::ParanoidNumber(const string & str) : m_value(0), m_next() while (str[end] != '\0') ++end; ParanoidNumber m(1); - for (int i = dp-1; i >= 0; --i) + for (int i = dp-1; i >= negate; --i) { ParanoidNumber b(str[i]-'0'); b*=m; @@ -60,6 +66,14 @@ ParanoidNumber::ParanoidNumber(const string & str) : m_value(0), m_next() b*=n; this->operator+=(b); } + + if (negate) + Negate(); + + #ifdef PARANOID_COMPARE_EPSILON + double d = strtod(str.c_str(), NULL); + CompareForSanity(d, d); + #endif } ParanoidNumber & ParanoidNumber::operator=(const ParanoidNumber & a) @@ -916,6 +930,12 @@ bool ParanoidNumber::SanityCheck(set & visited) const return true; } +void ParanoidNumber::Negate() +{ + swap(m_next[ADD], m_next[SUBTRACT]); + m_value = -m_value; +} + #ifdef PARANOID_USE_ARENA void * ParanoidNumber::operator new(size_t s) diff --git a/src/paranoidnumber.h b/src/paranoidnumber.h index 6ad24f2..e57d00c 100644 --- a/src/paranoidnumber.h +++ b/src/paranoidnumber.h @@ -24,7 +24,7 @@ // Define to compare all ops against double ops and check within epsilon #define PARANOID_COMPARE_EPSILON 1e-6 -#define CompareForSanity(...) this->ParanoidNumber::CompareForSanityEx(__func__, __FILE__, __LINE__, __VA_ARGS__) +#define CompareForSanity(...) ParanoidNumber::CompareForSanityEx(__func__, __FILE__, __LINE__, __VA_ARGS__) namespace IPDF { @@ -111,6 +111,9 @@ namespace IPDF m_next[i].push_back(new ParanoidNumber(*next)); // famous last words... } } + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(cpy.Digit(), cpy.Digit()); + #endif //assert(SanityCheck()); } @@ -171,47 +174,60 @@ namespace IPDF // None of these are actually const - bool operator<(const ParanoidNumber & a) const {return ToDouble() < a.ToDouble();} - bool operator<=(const ParanoidNumber & a) const {return this->operator<(a) || this->operator==(a);} - bool operator>(const ParanoidNumber & a) const {return !(this->operator<=(a));} - bool operator>=(const ParanoidNumber & a) const {return !(this->operator<(a));} - bool operator==(const ParanoidNumber & a) const {return ToDouble() == a.ToDouble();} - bool operator!=(const ParanoidNumber & a) const {return !(this->operator==(a));} + bool operator<(const ParanoidNumber & a) const {return Digit() < a.Digit();} + bool operator<=(const ParanoidNumber & a) const {return Digit() <= a.Digit();} + bool operator>(const ParanoidNumber & a) const {return Digit() > a.Digit();} + bool operator>=(const ParanoidNumber & a) const {return Digit() >= a.Digit();} + bool operator==(const ParanoidNumber & a) const {return Digit() == a.Digit();} + bool operator!=(const ParanoidNumber & a) const {return Digit() != a.Digit();} ParanoidNumber operator-() const { - ParanoidNumber neg(0); - neg -= *this; + ParanoidNumber neg(*this); + neg.Negate(); + #ifdef PARANOID_COMPARE_EPSILON + neg.CompareForSanity(-Digit(), Digit()); + #endif return neg; } + void Negate(); + + ParanoidNumber operator+(const ParanoidNumber & a) const { ParanoidNumber result(*this); - a.SanityCheck(); result += a; + #ifdef PARANOID_COMPARE_EPSILON + result.CompareForSanity(Digit()+a.Digit(), a.Digit()); + #endif return result; } ParanoidNumber operator-(const ParanoidNumber & a) const { ParanoidNumber result(*this); result -= a; + #ifdef PARANOID_COMPARE_EPSILON + result.CompareForSanity(Digit()-a.Digit(), a.Digit()); + #endif return result; } ParanoidNumber operator*(const ParanoidNumber & a) const { ParanoidNumber result(*this); result *= a; - if (!result.SanityCheck()) - { - Fatal("Blargh"); - } + #ifdef PARANOID_COMPARE_EPSILON + result.CompareForSanity(Digit()*a.Digit(), a.Digit()); + #endif return result; } ParanoidNumber operator/(const ParanoidNumber & a) const { ParanoidNumber result(*this); result /= a; + #ifdef PARANOID_COMPARE_EPSILON + result.CompareForSanity(Digit()/a.Digit(), a.Digit()); + #endif return result; } diff --git a/src/tests/realops.cpp b/src/tests/realops.cpp index dd45aa8..3c863a1 100644 --- a/src/tests/realops.cpp +++ b/src/tests/realops.cpp @@ -25,6 +25,8 @@ int main(int argc, char ** argv) DebugRealInfo(); unsigned failures = 0; + Real acumulate(0); + double dacumulate = 0; for (unsigned i = 0; i < TEST_CASES; ++i) { //Debug("Test %u of %u", i, TEST_CASES); @@ -129,6 +131,49 @@ int main(int argc, char ** argv) Warn("a = b = %f should be %f, a before op was %f", Double(a), da, Double(abeforeop)); } + if (NotEqual(Double(-a), -da)) + { + failures++; + Warn("-a = %f should be %f, a before op was %f", Double(-a), -da, Double(abeforeop)); + } + + if (NotEqual(Double(Sqrt(a)), Sqrt(da))) + { + failures++; + Warn("Sqrt(a) = %f should be %f, a before op was %f", Double(Sqrt(a)), Sqrt(da), Double(abeforeop)); + } + + if (NotEqual(Double(a), da)) + { + failures++; + Warn("a = %f, should be %f, a before ops was %f", Double(a), da, Double(abeforeop)); + } + + switch (rand() % 4) + { + case 0: + acumulate += a; + dacumulate += da; + break; + case 1: + acumulate -= a; + dacumulate -= da; + break; + case 2: + acumulate *= a; + dacumulate *= da; + break; + case 3: + acumulate /= a; + dacumulate /= da; + break; + } + if (NotEqual(Double(acumulate), dacumulate)) + { + Warn("Accumulated result %.30lf vs %.30lf is wrong", Double(acumulate), dacumulate); + failures++; + } + if (failures > old_failures) { Error("%u failures on case %u da = %f, db = %f, a = %f, b = %f, aa = %f, bb = %f", failures-old_failures, i, da, db, Double(a), Double(b), Double(aa), Double(bb)); @@ -136,8 +181,12 @@ int main(int argc, char ** argv) Debug("\tStrings are a = %s, b = %s, aa = %s, bb = %s", a.Str().c_str(), b.Str().c_str(), aa.Str().c_str(), bb.Str().c_str()); #endif } + } - Debug("Completed %u test cases with total of %u operations, %u failures", TEST_CASES, 12*TEST_CASES, failures); + Debug("Completed %u test cases with total of %u operations, %u failures", TEST_CASES, 18*TEST_CASES, failures); Debug("Total accumulated difference between Real and Double operations was %f", g_totalerror); + Debug("Real: %.40lf", Double(acumulate)); + Debug("Doub: %.40lf", dacumulate); + Debug("Diff: %.40lf", Double(acumulate) - dacumulate); }