+ 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 (false)//(!CloseEnough(d, p))
+ {
+ Debug("%.40lf %c= %.40lf failed", p0, OpChar(op), amount);
+ Debug("%.40lf vs %.40lf", 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("PN is: %s", p.Str().c_str());
+
+ 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 = 1; //* (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());
+ }
+ if (i == test_cases - 1)
+ {
+ Debug("double: %.40lf", da);
+ Debug("PN: %.40lf", a.ToDouble());
+ }
+ }
+
+