X-Git-Url: https://git.ucc.asn.au/?p=ipdf%2Fcode.git;a=blobdiff_plain;f=src%2Ftests%2Fparanoidtester.cpp;h=47969b980ce32e40b358d8bda6c8572d47c2c4cf;hp=98c056d7376f9554ec7a789edb04693c567defce;hb=77137590512d969da2d54d9ba53d76836a290c6a;hpb=20788af97c06b76040ea2de5ab3ddc683a261365 diff --git a/src/tests/paranoidtester.cpp b/src/tests/paranoidtester.cpp index 98c056d..47969b9 100644 --- a/src/tests/paranoidtester.cpp +++ b/src/tests/paranoidtester.cpp @@ -13,7 +13,7 @@ using namespace std; using namespace IPDF; -string RandomNumberAsString(int max_digits = 12) +string RandomNumberAsString(int max_digits = 3) { string result(""); int digits = 1+(rand() % max_digits); @@ -30,31 +30,219 @@ string RandomNumberAsString(int max_digits = 12) return result; } -bool CloseEnough(double d, ParanoidNumber & p) +bool CloseEnough(long double d, ParanoidNumber & p, long double eps = 1e-6) { - double pd = p.ToDouble(); + long double pd = p.Convert(); if (d == 0) - return fabs(pd) <= 1e-6; - return fabs((fabs(pd - d) / d)) <= 1e-6; + return fabs(pd) <= eps; + return fabs((fabs(pd - d) / d)) <= eps; +} + +void TestOp(ParanoidNumber & p, double & d, Optype op, const double amount) +{ + string p0str(p.Str()); + double p0 = p.ToDouble(); + switch (op) + { + case ADD: + p += amount; + d += amount; + break; + case SUBTRACT: + p -= amount; + d -= amount; + break; + case MULTIPLY: + p *= amount; + d *= amount; + break; + case DIVIDE: + p /= amount; + d /= amount; + break; + default: + break; + } + if (!CloseEnough(d, p)) + { + Debug("%lf %c= %lf failed", p0, OpChar(op), amount); + Debug("%lf vs %lf", p.ToDouble(), d); + Debug("Before: {%s}\n", p0str.c_str()); + Debug("After: {%s}\n", p.Str().c_str()); + Fatal(":-("); + } + +} + +void TestAddSubIntegers(int max=100) +{ + Debug("Test add/sub integers 0 -> %i", max); + ParanoidNumber p; + double d(0); + for (int a = 0; a < max; ++a) + { + TestOp(p, d, ADD, a); + for (int b = 0; b < max; ++b) + { + TestOp(p, d, SUBTRACT, b); + } + for (int b = 0; b < max; ++b) + { + TestOp(p, d, ADD, b); + } + } + for (int a = 0; a < max; ++a) + { + TestOp(p, d, SUBTRACT, a); + for (int b = 0; b < max; ++b) + { + TestOp(p, d, ADD, b); + } + for (int b = 0; b < max; ++b) + { + TestOp(p, d, SUBTRACT, b); + } + } + Debug("PN Yields: %.40lf", p.ToDouble()); + Debug("Doubles Yield: %.40lf", d); + Debug("Complete!"); + +} + +void TestMulDivIntegers(int max=50) +{ + Debug("Test mul/div integers 1 -> %i", max); + ParanoidNumber p(1.0); + double d(1.0); + for (int a = 1; a < max; ++a) + { + TestOp(p, d, MULTIPLY, a); + for (int b = 1; b < max; ++b) + { + TestOp(p, d, DIVIDE, b); + } + for (int b = 1; b < max; ++b) + { + TestOp(p, d, MULTIPLY, b); + } + } + for (int a = 1; a < max; ++a) + { + TestOp(p, d, DIVIDE, a); + for (int b = 1; b < max; ++b) + { + TestOp(p, d, MULTIPLY, b); + } + for (int b = 1; b < max; ++b) + { + TestOp(p, d, DIVIDE, b); + } + } + Debug("PN Yields: %.40lf", p.ToDouble()); + Debug("Doubles Yield: %.40lf", d); + Debug("Complete!"); + +} + +void TestRandomisedOps(int test_cases = 1000, int ops_per_case = 1, int max_digits = 4) +{ + Debug("Test %i*%i randomised ops (max digits = %i)", test_cases, ops_per_case, max_digits); + long double eps = 1e-2; //* (1e4*ops_per_case); + for (int i = 0; i < test_cases; ++i) + { + string s = RandomNumberAsString(max_digits); + ParanoidNumber a(s); + + double da(a.ToDouble()); + for (int j = 1; j <= ops_per_case; ++j) + { + double da2(a.ToDouble()); + s = RandomNumberAsString(max_digits); + ParanoidNumber b(s); + double db(b.ToDouble()); + + + + Optype op = Optype(rand() % 4); + + ParanoidNumber a_before(a); + + + switch (op) + { + case ADD: + a += b; + da += db; + da2 += db; + break; + case SUBTRACT: + a -= b; + da -= db; + da2 -= db; + break; + case MULTIPLY: + a *= b; + da *= db; + da2 *= db; + break; + case DIVIDE: + if (db == 0) + { + --i; + } + else + { + a /= b; + da /= db; + da2 /= db; + } + break; + case NOP: + break; + } + if (!CloseEnough(da2, a, eps)) + { + Error("{%s} %c= {%s}", a_before.Str().c_str(), OpChar(op), b.Str().c_str()); + Error("{%s}", a.Str().c_str()); + Error("double Yields: %.40lf", da); + Error("PN Yields: %.40lf", a.ToDouble()); + Fatal("Failed on case %i", i*ops_per_case + j-1); + } + } + if (!CloseEnough(da, a, eps)) + { + Warn("double Yields: %.40lf", da); + Warn("PN Yields: %.40lf", a.ToDouble()); + } + } + Debug("Complete!"); + } #define TEST_CASES 1000 int main(int argc, char ** argv) { - srand(time(NULL)); + TestAddSubIntegers(); + TestMulDivIntegers(); + for (int i = 1; i <= 100; ++i) + TestRandomisedOps(1000, i); + return 0; + srand(0);//time(NULL)); //always test off same set string number(RandomNumberAsString()); ParanoidNumber a(number); + float fa = strtof(number.c_str(), NULL); double da = strtod(number.c_str(), NULL); double diff = 0; long double lda = strtold(number.c_str(), NULL); - + Debug("a is %s", a.Str().c_str()); if (fabs(a.ToDouble() - da) > 1e-6) { Error("double %lf, pn %lf {%s}", da, a.ToDouble(), a.Str().c_str()); Fatal("Didn't construct correctly off %s", number.c_str()); + } char opch[] = {'+','-','*','/'}; @@ -109,14 +297,15 @@ int main(int argc, char ** argv) break; } diff = 100.0*(fabs(a.ToDouble() - da) / da); - if (!CloseEnough(da, a)) + if (!CloseEnough(lda, a)) { Error("Op %i: ParanoidNumber probably doesn't work", i); Error("Operation: %lf %c %lf", oldda, opch[op], db); Error("As PN: %lf %c %lf", olda.ToDouble(), opch[op], b.ToDouble()); + Error("PN String before: %s", olda.Str().c_str()); Error("PN String: %s", a.Str().c_str()); - Error("Diff is %.40lf", diff); - Fatal("%.40lf, expected aboout %.40lf", a.ToDouble(), da); + Error("LONG double gives %.40llf", lda); + Fatal("%.40llf, expected aboout %.40llf", a.Convert(), lda); }