Slightly better results
[ipdf/code.git] / src / tests / paranoidtester.cpp
1 #include "main.h"
2 #include "real.h"
3 #include <cmath>
4 #include <cassert>
5 #include <list>
6 #include <bitset>
7 #include <iostream>
8 #include <cfloat>
9 #include <fenv.h>
10 #include "paranoidnumber.h"
11 #include "progressbar.h"
12
13 using namespace std;
14 using namespace IPDF;
15
16 string RandomNumberAsString(int max_digits = 3)
17 {
18         string result("");
19         int digits = 1+(rand() % max_digits);
20         int dp = (rand() % digits)+1;
21         for (int i = 0; i < digits; ++i)
22         {
23                 if (i == dp)
24                 {
25                         result += ".";
26                         continue;
27                 }
28                 result += ('0'+rand() % 10);
29         }
30         return result;
31 }
32
33 bool CloseEnough(long double d, ParanoidNumber & p, long double eps = 1e-6)
34 {
35         long double pd = p.Convert<long double>();
36                 
37         if (d == 0)
38                 return fabs(pd) <= eps;
39         return fabs((fabs(pd - d) / d)) <= eps;
40 }
41
42 void TestOp(ParanoidNumber & p, double & d, Optype op, const double amount)
43 {
44         string p0str(p.Str());
45         double p0 = p.ToDouble();
46         switch (op)
47         {
48                 case ADD:
49                         p += amount;
50                         d += amount;
51                         break;
52                 case SUBTRACT:
53                         p -= amount;
54                         d -= amount;
55                         break;
56                 case MULTIPLY:
57                         p *= amount;
58                         d *= amount;
59                         break;
60                 case DIVIDE:
61                         p /= amount;
62                         d /= amount;
63                         break;
64                 default:
65                         break;
66         }
67         if (!CloseEnough(d, p))
68         {
69                 Debug("%lf %c= %lf failed", p0, OpChar(op), amount);
70                 Debug("%lf vs %lf", p.ToDouble(), d);
71                 Debug("Before: {%s}\n", p0str.c_str());
72                 Debug("After: {%s}\n", p.Str().c_str());
73                 Fatal(":-(");
74         }
75
76 }
77
78 void TestAddSubIntegers(int max=100)
79 {
80         Debug("Test add/sub integers 0 -> %i", max);
81         ParanoidNumber p;
82         double d(0);
83         for (int a = 0; a < max; ++a)
84         {
85                 TestOp(p, d, ADD, a);
86                 for (int b = 0; b < max; ++b)
87                 {
88                         TestOp(p, d, SUBTRACT, b);
89                 }
90                 for (int b = 0; b < max; ++b)
91                 {
92                         TestOp(p, d, ADD, b);
93                 }
94         }
95         for (int a = 0; a < max; ++a)
96         {
97                 TestOp(p, d, SUBTRACT, a);
98                 for (int b = 0; b < max; ++b)
99                 {
100                         TestOp(p, d, ADD, b);
101                 }
102                 for (int b = 0; b < max; ++b)
103                 {
104                         TestOp(p, d, SUBTRACT, b);
105                 }
106         }
107         Debug("PN Yields: %.40lf", p.ToDouble());
108         Debug("Doubles Yield: %.40lf", d);
109         Debug("Complete!");
110
111 }
112
113 void TestMulDivIntegers(int max=50)
114 {
115         Debug("Test mul/div integers 1 -> %i", max);
116         ParanoidNumber p(1.0);
117         double d(1.0);
118         for (int a = 1; a < max; ++a)
119         {
120                 TestOp(p, d, MULTIPLY, a);
121                 for (int b = 1; b < max; ++b)
122                 {
123                         TestOp(p, d, DIVIDE, b);
124                 }
125                 for (int b = 1; b < max; ++b)
126                 {
127                         TestOp(p, d, MULTIPLY, b);
128                 }
129         }
130         for (int a = 1; a < max; ++a)
131         {
132                 TestOp(p, d, DIVIDE, a);
133                 for (int b = 1; b < max; ++b)
134                 {
135                         TestOp(p, d, MULTIPLY, b);
136                 }
137                 for (int b = 1; b < max; ++b)
138                 {
139                         TestOp(p, d, DIVIDE, b);
140                 }
141         }
142         Debug("PN Yields: %.40lf", p.ToDouble());
143         Debug("Doubles Yield: %.40lf", d);
144         Debug("Complete!");
145
146 }
147
148 void TestRandomisedOps(int test_cases = 1000, int ops_per_case = 1, int max_digits = 4)
149 {
150         Debug("Test %i*%i randomised ops (max digits = %i)", test_cases, ops_per_case, max_digits);
151         long double eps = 1e-2; //* (1e4*ops_per_case);
152         for (int i = 0; i < test_cases; ++i)
153         {
154                 string s = RandomNumberAsString(max_digits);
155                 ParanoidNumber a(s);
156                 
157                 double da(a.ToDouble());                
158                 for (int j = 1; j <= ops_per_case; ++j)
159                 {
160                         double da2(a.ToDouble());
161                         s = RandomNumberAsString(max_digits);
162                         ParanoidNumber b(s);
163                         double db(b.ToDouble());
164         
165                 
166         
167                         Optype op = Optype(rand() % 4);
168                         
169                         ParanoidNumber a_before(a);
170                         
171                 
172                         switch (op)
173                         {
174                         case ADD:
175                                 a += b;
176                                 da += db;
177                                 da2 += db;
178                                 break;
179                         case SUBTRACT:
180                                 a -= b;
181                                 da -= db;
182                                 da2 -= db;
183                                 break;
184                         case MULTIPLY:
185                                 a *= b;
186                                 da *= db;
187                                 da2 *= db;
188                                 break;
189                         case DIVIDE:
190                                 if (db == 0)
191                                 {
192                                         --i;
193                                 }
194                                 else
195                                 {
196                                         a /= b;
197                                         da /= db;
198                                         da2 /= db;
199                                 }
200                                 break;
201                         case NOP:
202                                 break;
203                         }
204                         if (!CloseEnough(da2, a, eps))
205                         {
206                                 Error("{%s} %c= {%s}", a_before.Str().c_str(), OpChar(op), b.Str().c_str());
207                                 Error("{%s}", a.Str().c_str());
208                                 Error("double Yields: %.40lf", da);
209                                 Error("PN Yields: %.40lf", a.ToDouble());
210                                 Fatal("Failed on case %i", i*ops_per_case + j-1);
211                         }
212                 }
213                 if (!CloseEnough(da, a, eps))
214                 {
215                         Warn("double Yields: %.40lf", da);
216                         Warn("PN Yields: %.40lf", a.ToDouble());
217                 }
218         }
219         Debug("Complete!");
220
221 }
222
223 #define TEST_CASES 1000
224
225 int main(int argc, char ** argv)
226 {
227         TestAddSubIntegers();
228         TestMulDivIntegers();
229         for (int i = 1; i <= 100; ++i)
230                 TestRandomisedOps(1000, i);
231         return 0;
232         srand(0);//time(NULL)); //always test off same set
233         string number(RandomNumberAsString());
234         ParanoidNumber a(number);
235
236         float fa = strtof(number.c_str(), NULL);
237         double da = strtod(number.c_str(), NULL);
238         double diff = 0;
239         long double lda = strtold(number.c_str(), NULL);
240         Debug("a is %s", a.Str().c_str());
241         if (fabs(a.ToDouble() - da) > 1e-6)
242         {
243                 Error("double %lf, pn %lf {%s}", da, a.ToDouble(), a.Str().c_str());
244                 Fatal("Didn't construct correctly off %s", number.c_str());
245                 
246         }
247         
248         char opch[] = {'+','-','*','/'};
249         int opcount[] = {0,0,0,0};
250         for (int i = 0; i < TEST_CASES; ++i)
251         {
252                 ProgressBar(i, TEST_CASES); fprintf(stderr, "%.30lf (%d)+ (%d)- (%d)* (%d)/ [Paranoia %ld]",diff, opcount[0],opcount[1],opcount[2],opcount[3], ParanoidNumber::Paranoia());
253                 number = RandomNumberAsString();
254                 ParanoidNumber b(number);
255                 float fb = strtof(number.c_str(), NULL);
256                 double db = strtod(number.c_str(), NULL);
257                 if (db == 0)
258                 {
259                         --i;
260                         continue;
261                 }
262                 long double ldb = strtold(number.c_str(), NULL);
263                 int op = rand() % 4;//= 2*(rand() % 2);
264                 while (op == 1)
265                         op = rand() % 4;
266                 //if (rand() % 2 == 0)
267                         //op = 2+(rand() % 2);
268                         
269                 opcount[op]++;
270                 ParanoidNumber olda(a);
271                 double oldda(da);
272                 switch (op)
273                 {
274                         case 0:
275                                 a += b;
276                                 fa += fb;
277                                 da += db;
278                                 lda += ldb;
279                                 break;
280                         case 1:
281                                 a -= b;
282                                 fa -= fb;
283                                 da -= db;
284                                 lda -= ldb;
285                                 break;
286                         case 2:
287                                 a *= b;
288                                 fa *= fb;
289                                 da *= db;
290                                 lda *= ldb;
291                                 break;
292                         case 3:
293                                 a /= b;
294                                 fa /= fb;
295                                 da /= db;
296                                 lda /= ldb;
297                                 break;
298                 }
299                 diff = 100.0*(fabs(a.ToDouble() - da) / da);
300                 if (!CloseEnough(lda, a))
301                 {
302                         Error("Op %i: ParanoidNumber probably doesn't work", i);
303                         Error("Operation: %lf %c %lf", oldda, opch[op], db);
304                         Error("As PN: %lf %c %lf", olda.ToDouble(), opch[op], b.ToDouble());
305                         Error("PN String before: %s", olda.Str().c_str());
306                         Error("PN String: %s", a.Str().c_str());
307                         Error("LONG double gives %.40llf", lda);
308                         Fatal("%.40llf, expected aboout %.40llf", a.Convert<long double>(), lda);
309                         
310                         
311                 }
312                 
313         }
314         printf("ParanoidNumber: {%s} = %.40lf\n", a.Str().c_str(), a.ToDouble());
315         printf("float: %.40f\n", fa);
316         printf("double: %.40lf\n", da);
317         printf("long double: %.40Lf\n", lda);
318         printf("diff %.40lf\n", diff);
319 }

UCC git Repository :: git.ucc.asn.au