QOF: test-numeric.c Source File

QOF 0.7.5

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

test-numeric.c
1 /***************************************************************************
2 * test-numeric.c
3 *
4 * Test file created by Linas Vepstas <linas@linas.org>
5 * Review operation of the gnc-numeric tools by verifying results
6 * of various operations.
7 *
8 * June 2004
9 * Copyright 2004 Linas Vepstas <linas@linas.org>
10 ****************************************************************************/
11 /*
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 * 02110-1301, USA.
26 */
27
28 #include <ctype.h>
29 #include <glib.h>
30 #include "qof.h"
31 #include "test-stuff.h"
32 #include "test-engine-stuff.h"
33 #include "qofnumeric.h"
34
35 #define NREPS 2000
36
37 static char *
38 qof_numeric_print (QofNumeric in)
39 {
40 char *retval;
41 if (qof_numeric_check (in))
42 {
43 retval =
44 g_strdup_printf ("<ERROR> [%" G_GINT64_FORMAT " / %"
45 G_GINT64_FORMAT "]", in.num, in.denom);
46 }
47 else
48 {
49 retval =
50 g_strdup_printf ("[%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]",
51 in.num, in.denom);
52 }
53 return retval;
54 }
55
56 /* ======================================================= */
57
58 static void
59 check_unary_op (gboolean (*eqtest) (QofNumeric, QofNumeric),
60 QofNumeric expected,
61 QofNumeric actual, QofNumeric input, const gchar *errmsg)
62 {
63 gchar *e = qof_numeric_print (expected);
64 gchar *r = qof_numeric_print (actual);
65 gchar *a = qof_numeric_print (input);
66 gchar *str = g_strdup_printf (errmsg, e, r, a);
67
68 do_test (eqtest (expected, actual), str);
69
70 g_free (a);
71 g_free (r);
72 g_free (e);
73 g_free (str);
74 }
75
76 /* ======================================================= */
77
78 static void
79 check_binary_op (QofNumeric expected,
80 QofNumeric actual,
81 QofNumeric input_a, QofNumeric input_b, const gchar *errmsg)
82 {
83 gchar *e = qof_numeric_print (expected);
84 gchar *r = qof_numeric_print (actual);
85 gchar *a = qof_numeric_print (input_a);
86 gchar *b = qof_numeric_print (input_b);
87 gchar *str = g_strdup_printf (errmsg, e, r, a, b);
88
89 do_test (qof_numeric_eq (expected, actual), str);
90
91 g_free (a);
92 g_free (b);
93 g_free (r);
94 g_free (e);
95 g_free (str);
96 }
97
98 /* ======================================================= */
99
100 static gboolean
101 qof_numeric_unequal (QofNumeric a, QofNumeric b)
102 {
103 return (0 == qof_numeric_equal (a, b));
104 }
105
106 /* ======================================================= */
107
108 /* Make sure that the equivalence operator we use for
109 * later tests actually works */
110 static void
111 check_eq_operator (void)
112 {
113 QofNumeric a = qof_numeric_create (42, 58);
114 QofNumeric b = qof_numeric_create (42, 58);
115 QofNumeric c = qof_numeric_create (40, 58);
116
117 /* Check strict equivalence and non-equivalence */
118 do_test (qof_numeric_eq (a, a), "expected self-equivalence");
119 do_test (qof_numeric_eq (a, b), "expected equivalence");
120 do_test (0 == qof_numeric_eq (a, c), "expected inequivalence");
121 }
122
123 /* ======================================================= */
124
125 static void
126 check_reduce (void)
127 {
128 QofNumeric one, rone;
129 QofNumeric four, rfour;
130 QofNumeric val, rval;
131 /* Check common factor elimination (needed for equality checks) */
132 one = qof_numeric_create (1, 1);
133 rone = qof_numeric_create (1000000, 1000000);
134 rone = qof_numeric_reduce (rone);
135 do_test (qof_numeric_eq (one, rone), "reduce to one");
136
137 four = qof_numeric_create (4, 1);
138 rfour = qof_numeric_create (480, 120);
139 rfour = qof_numeric_reduce (rfour);
140 do_test (qof_numeric_eq (four, rfour), "reduce to four");
141
142 val = qof_numeric_create (10023234LL, 334216654LL);
143 rval = qof_numeric_reduce (val);
144 check_unary_op (qof_numeric_eq,
145 qof_numeric_create (5011617, 167108327),
146 rval,
147 val, "check_reduce(1) expected %s = %s = reduce(%s)");
148
149 val = qof_numeric_create (17474724864LL, 136048896LL);
150 rval = qof_numeric_reduce (val);
151 check_unary_op (qof_numeric_eq,
152 qof_numeric_create (4 * 17 * 17, 9),
153 rval,
154 val, "check_reduce(2) expected %s = %s = reduce(%s)");
155
156 val = qof_numeric_create (1024LL, 1099511627776LL);
157 rval = qof_numeric_reduce (val);
158 check_unary_op (qof_numeric_eq,
159 qof_numeric_create (1, 1024 * 1024 * 1024),
160 rval,
161 val, "check_reduce(3): expected %s = %s = reduce(%s)");
162 }
163
164 /* ======================================================= */
165
166 static void
167 check_equality_operator (void)
168 {
169 gint i, m;
170 gint mult;
171 gint64 f, deno, numer;
172 QofNumeric big, rbig;
173 QofNumeric val, mval;
174 QofNumeric bval, rval;
175 /* Check equality operator for some large numer/denom values */
176 numer = 1 << 30;
177 numer <<= 30; /* we don't trust cpp to compute 1<<60 correctly */
178 deno = 1 << 30;
179 deno <<= 20;
180 rbig = qof_numeric_create (numer, deno);
181
182 big = qof_numeric_create (1 << 10, 1);
183 do_test (qof_numeric_equal (big, rbig), "equal to billion");
184
185 big = qof_numeric_create (1 << 20, 1 << 10);
186 do_test (qof_numeric_equal (big, rbig), "equal to 1<<20/1<<10");
187
188 big = qof_numeric_create (1 << 30, 1 << 20);
189 do_test (qof_numeric_equal (big, rbig), "equal to 1<<30/1<<20");
190
191 numer = 1 << 30;
192 numer <<= 30; /* we don't trust cpp to compute 1<<60 correctly */
193 deno = 1 << 30;
194 rbig = qof_numeric_create (numer, deno);
195
196 big = qof_numeric_create (1 << 30, 1);
197 do_test (qof_numeric_equal (big, rbig), "equal to 1<<30");
198
199 numer = 1 << 30;
200 numer <<= 10;
201 big = qof_numeric_create (numer, 1 << 10);
202 do_test (qof_numeric_equal (big, rbig), "equal to 1<<40/1<<10");
203
204 numer <<= 10;
205 big = qof_numeric_create (numer, 1 << 20);
206 do_test (qof_numeric_equal (big, rbig), "equal to 1<<50/1<<20");
207
208 /* We assume RAND_MAX is less that 1<<32 */
209 for (i = 0; i < NREPS; i++)
210 {
211 deno = rand () / 2;
212 mult = rand () / 2;
213 numer = rand () / 2;
214
215 val = qof_numeric_create (numer, deno);
216 mval = qof_numeric_create (numer * mult, deno * mult);
217
218 /* The reduced version should be equivalent */
219 bval = qof_numeric_reduce (val);
220 rval = qof_numeric_reduce (mval);
221 check_unary_op (qof_numeric_eq,
222 bval, rval, mval, "expected %s = %s = reduce(%s)");
223
224 /* The unreduced versions should be equal */
225 check_unary_op (qof_numeric_equal,
226 val, mval, mval, "expected %s = %s");
227
228 /* Certain modulo's should be very cleary un-equal; this
229 * helps stop funky modulo-64 aliasing in compares that
230 * might creep in. */
231 mval.denom >>= 1;
232 mval.num >>= 1;
233 m = 0;
234 f = mval.denom;
235 while (f % 2 == 0)
236 {
237 f >>= 1;
238 m++;
239 }
240 if (1 < m)
241 {
242 gint64 nn = 1 << (32 - m);
243 nn <<= 32;
244 nn += mval.num;
245 val = qof_numeric_create (2 * nn, 2 * mval.denom);
246 check_unary_op (qof_numeric_unequal,
247 val, mval, mval, "expected unequality %s != %s");
248
249 }
250 }
251 }
252
253 /* ======================================================= */
254
255 static void
256 check_rounding (void)
257 {
258 QofNumeric val;
259
260 val = qof_numeric_create (7, 16);
261 check_unary_op (qof_numeric_eq,
262 qof_numeric_create (43, 100),
263 qof_numeric_convert (val, 100, QOF_HOW_RND_FLOOR),
264 val, "expected %s = %s = (%s as 100th's floor)");
265 check_unary_op (qof_numeric_eq,
266 qof_numeric_create (44, 100),
267 qof_numeric_convert (val, 100, QOF_HOW_RND_CEIL),
268 val, "expected %s = %s = (%s as 100th's ceiling)");
269 check_unary_op (qof_numeric_eq,
270 qof_numeric_create (43, 100),
271 qof_numeric_convert (val, 100, QOF_HOW_RND_TRUNC),
272 val, "expected %s = %s = (%s as 100th's trunc)");
273 check_unary_op (qof_numeric_eq,
274 qof_numeric_create (44, 100),
275 qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
276 val, "expected %s = %s = (%s as 100th's round)");
277
278 val = qof_numeric_create (1511, 1000);
279 check_unary_op (qof_numeric_eq,
280 qof_numeric_create (151, 100),
281 qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
282 val, "expected %s = %s = (%s as 100th's round)");
283
284 val = qof_numeric_create (1516, 1000);
285 check_unary_op (qof_numeric_eq,
286 qof_numeric_create (152, 100),
287 qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
288 val, "expected %s = %s = (%s as 100th's round)");
289
290 /* Half-values always get rounded to nearest even number */
291 val = qof_numeric_create (1515, 1000);
292 check_unary_op (qof_numeric_eq,
293 qof_numeric_create (152, 100),
294 qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
295 val, "expected %s = %s = (%s as 100th's round)");
296
297 val = qof_numeric_create (1525, 1000);
298 check_unary_op (qof_numeric_eq,
299 qof_numeric_create (152, 100),
300 qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
301 val, "expected %s = %s = (%s as 100th's round)");
302
303 val = qof_numeric_create (1535, 1000);
304 check_unary_op (qof_numeric_eq,
305 qof_numeric_create (154, 100),
306 qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
307 val, "expected %s = %s = (%s as 100th's round)");
308
309 val = qof_numeric_create (1545, 1000);
310 check_unary_op (qof_numeric_eq,
311 qof_numeric_create (154, 100),
312 qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
313 val, "expected %s = %s = (%s as 100th's round)");
314 }
315
316 /* ======================================================= */
317
318 static void
319 check_double (void)
320 {
321 gdouble flo;
322 QofNumeric val = qof_numeric_create (0, 1);
323
324 check_unary_op (qof_numeric_eq,
325 qof_numeric_create (112346, 100000),
326 qof_numeric_from_double (1.1234567890123,
327 QOF_DENOM_AUTO,
328 QOF_HOW_DENOM_SIGFIGS (6) |
329 QOF_HOW_RND_ROUND),
330 val, "expected %s = %s double 6 figs");
331
332 check_unary_op (qof_numeric_eq,
333 qof_numeric_create (112346, 10000000),
334 qof_numeric_from_double (0.011234567890123,
335 QOF_DENOM_AUTO,
336 QOF_HOW_DENOM_SIGFIGS (6) |
337 QOF_HOW_RND_ROUND),
338 val, "expected %s = %s double 6 figs");
339
340 check_unary_op (qof_numeric_eq,
341 qof_numeric_create (112346, 100),
342 qof_numeric_from_double (1123.4567890123,
343 QOF_DENOM_AUTO,
344 QOF_HOW_DENOM_SIGFIGS (6) |
345 QOF_HOW_RND_ROUND),
346 val, "expected %s = %s double 6 figs");
347 check_unary_op (qof_numeric_eq,
348 qof_numeric_create (112346, 10000000000LL),
349 qof_numeric_from_double (1.1234567890123e-5,
350 QOF_DENOM_AUTO,
351 QOF_HOW_DENOM_SIGFIGS (6) |
352 QOF_HOW_RND_ROUND),
353 val, "expected %s = %s double 6 figs");
354
355 flo = qof_numeric_to_double (qof_numeric_create (7, 16));
356 do_test ((0.4375 == flo), "float pt conversion");
357 }
358
359 /* ======================================================= */
360
361 static void
362 check_neg (void)
363 {
364 QofNumeric a = qof_numeric_create (2, 6);
365 QofNumeric b = qof_numeric_create (1, 4);
366 QofNumeric c = qof_numeric_neg (a);
367 QofNumeric d = qof_numeric_neg (b);
368
369 check_unary_op (qof_numeric_eq,
370 qof_numeric_create (-2, 6), c,
371 a, "expected %s = %s = -(%s)");
372
373 check_unary_op (qof_numeric_eq,
374 qof_numeric_create (-1, 4), d,
375 b, "expected %s = %s = -(%s)");
376
377 }
378
379 /* ======================================================= */
380
381 static void
382 check_add_subtract (void)
383 {
384 gint i;
385 QofNumeric a, b, c, d, z;
386 #if CHECK_ERRORS_TOO
387 QofNumeric c;
388 #endif
389
390 a = qof_numeric_create (2, 6);
391 b = qof_numeric_create (1, 4);
392
393 /* Well, actually 14/24 would be acceptable/better in this case */
394 check_binary_op (qof_numeric_create (7, 12),
395 qof_numeric_add (a, b, QOF_DENOM_AUTO,
396 QOF_HOW_DENOM_EXACT), a, b,
397 "expected %s got %s = %s + %s for add exact");
398
399 check_binary_op (qof_numeric_create (58, 100),
400 qof_numeric_add (a, b, 100, QOF_HOW_RND_ROUND),
401 a, b,
402 "expected %s got %s = %s + %s for add 100ths (banker's)");
403
404 check_binary_op (qof_numeric_create (5833, 10000),
405 qof_numeric_add (a, b, QOF_DENOM_AUTO,
406 QOF_HOW_DENOM_SIGFIGS (4) |
407 QOF_HOW_RND_ROUND),
408 a, b, "expected %s got %s = %s + %s for add 4 sig figs");
409
410 check_binary_op (qof_numeric_create (583333, 1000000),
411 qof_numeric_add (a, b, QOF_DENOM_AUTO,
412 QOF_HOW_DENOM_SIGFIGS (6) |
413 QOF_HOW_RND_ROUND),
414 a, b, "expected %s got %s = %s + %s for add 6 sig figs");
415
416 check_binary_op (qof_numeric_create (1, 12),
417 qof_numeric_sub (a, b, QOF_DENOM_AUTO,
418 QOF_HOW_DENOM_EXACT), a, b,
419 "expected %s got %s = %s - %s for sub exact");
420
421 /* We should try something trickier for reduce & lcd */
422 check_binary_op (qof_numeric_create (1, 12),
423 qof_numeric_sub (a, b, QOF_DENOM_AUTO,
424 QOF_HOW_DENOM_REDUCE), a, b,
425 "expected %s got %s = %s - %s for sub reduce");
426
427 check_binary_op (qof_numeric_create (1, 12),
428 qof_numeric_sub (a, b, QOF_DENOM_AUTO,
429 QOF_HOW_DENOM_LCD), a, b,
430 "expected %s got %s = %s - %s for sub reduce");
431
432 check_binary_op (qof_numeric_create (8, 100),
433 qof_numeric_sub (a, b, 100, QOF_HOW_RND_ROUND),
434 a, b,
435 "expected %s got %s = %s - %s for sub 100ths (banker's)");
436
437 /* ------------------------------------------------------------ */
438 /* This test has failed before */
439 c = qof_numeric_neg (a);
440 d = qof_numeric_neg (b);
441 z = qof_numeric_zero ();
442 check_binary_op (c, qof_numeric_add_fixed (z, c),
443 z, c, "expected %s got %s = %s + %s for add fixed");
444
445 check_binary_op (d, qof_numeric_add_fixed (z, d),
446 z, d, "expected %s got %s = %s + %s for add fixed");
447
448 /* ------------------------------------------------------------ */
449 /* Same as above, but with signs reviersed */
450 a = c;
451 b = d;
452 /* Well, actually 14/24 would be acceptable/better in this case */
453 check_binary_op (qof_numeric_create (-7, 12),
454 qof_numeric_add (a, b, QOF_DENOM_AUTO,
455 QOF_HOW_DENOM_EXACT), a, b,
456 "expected %s got %s = %s + %s for add exact");
457
458 check_binary_op (qof_numeric_create (-58, 100),
459 qof_numeric_add (a, b, 100, QOF_HOW_RND_ROUND),
460 a, b,
461 "expected %s got %s = %s + %s for add 100ths (banker's)");
462
463 check_binary_op (qof_numeric_create (-5833, 10000),
464 qof_numeric_add (a, b, QOF_DENOM_AUTO,
465 QOF_HOW_DENOM_SIGFIGS (4) |
466 QOF_HOW_RND_ROUND),
467 a, b, "expected %s got %s = %s + %s for add 4 sig figs");
468
469 check_binary_op (qof_numeric_create (-583333, 1000000),
470 qof_numeric_add (a, b, QOF_DENOM_AUTO,
471 QOF_HOW_DENOM_SIGFIGS (6) |
472 QOF_HOW_RND_ROUND),
473 a, b, "expected %s got %s = %s + %s for add 6 sig figs");
474
475 check_binary_op (qof_numeric_create (-1, 12),
476 qof_numeric_sub (a, b, QOF_DENOM_AUTO,
477 QOF_HOW_DENOM_EXACT), a, b,
478 "expected %s got %s = %s - %s for sub exact");
479
480 /* We should try something trickier for reduce & lcd */
481 check_binary_op (qof_numeric_create (-1, 12),
482 qof_numeric_sub (a, b, QOF_DENOM_AUTO,
483 QOF_HOW_DENOM_REDUCE), a, b,
484 "expected %s got %s = %s - %s for sub reduce");
485
486 check_binary_op (qof_numeric_create (-1, 12),
487 qof_numeric_sub (a, b, QOF_DENOM_AUTO,
488 QOF_HOW_DENOM_LCD), a, b,
489 "expected %s got %s = %s - %s for sub reduce");
490
491 check_binary_op (qof_numeric_create (-8, 100),
492 qof_numeric_sub (a, b, 100, QOF_HOW_RND_ROUND),
493 a, b,
494 "expected %s got %s = %s - %s for sub 100ths (banker's)");
495
496 /* ------------------------------------------------------------ */
497 #if CHECK_ERRORS_TOO
498 c = qof_numeric_add_with_error (a, b, 100, QOF_HOW_RND_ROUND, &err);
499 printf ("add 100ths/error : %s + %s = %s + (error) %s\n\n",
500 qof_numeric_print (a), qof_numeric_print (b),
501 qof_numeric_print (c), qof_numeric_print (err));
502
503 c = qof_numeric_sub_with_error (a, b, 100, QOf_HOW_RND_FLOOR, &err);
504 printf ("sub 100ths/error : %s - %s = %s + (error) %s\n\n",
505 qof_numeric_print (a), qof_numeric_print (b),
506 qof_numeric_print (c), qof_numeric_print (err));
507
508 #endif
509
510 /* ------------------------------------------------------------ */
511 /* Add and subtract some random numbers */
512 for (i = 0; i < NREPS; i++)
513 {
514 QofNumeric e;
515 gint64 deno = rand () + 1;
516 gint64 na = get_random_gint64 ();
517 gint64 nb = get_random_gint64 ();
518 gint64 ne;
519
520 /* avoid overflow; */
521 na /= 2;
522 nb /= 2;
523
524 a = qof_numeric_create (na, deno);
525 b = qof_numeric_create (nb, deno);
526
527 /* Add */
528 ne = na + nb;
529 e = qof_numeric_create (ne, deno);
530 check_binary_op (e,
531 qof_numeric_add (a, b, QOF_DENOM_AUTO,
532 QOF_HOW_DENOM_EXACT), a, b,
533 "expected %s got %s = %s + %s for exact addition");
534
535 /* Subtract */
536 ne = na - nb;
537 e = qof_numeric_create (ne, deno);
538 check_binary_op (e,
539 qof_numeric_sub (a, b, QOF_DENOM_AUTO,
540 QOF_HOW_DENOM_EXACT), a, b,
541 "expected %s got %s = %s - %s for exact subtraction");
542 }
543 }
544
545 /* ======================================================= */
546
547 static void
548 check_mult_div (void)
549 {
550 gint i, j;
551 gint64 v;
552 QofNumeric c, d;
553 QofNumeric amt_a, amt_tot, frac, val_tot, val_a;
554 QofNumeric a, b;
555
556 a = qof_numeric_create (-100, 100);
557 b = qof_numeric_create (1, 1);
558 check_binary_op (qof_numeric_create (-100, 100),
559 qof_numeric_div (a, b, QOF_DENOM_AUTO,
560 QOF_HOW_DENOM_EXACT), a, b,
561 "expected %s got %s = %s / %s div exact");
562
563 a = qof_numeric_create (-100, 100);
564 b = qof_numeric_create (-1, 1);
565 check_binary_op (qof_numeric_create (100, 100),
566 qof_numeric_div (a, b, QOF_DENOM_AUTO,
567 QOF_HOW_DENOM_EXACT), a, b,
568 "expected %s got %s = %s / %s div exact");
569
570 a = qof_numeric_create (-100, 100);
571 b = qof_numeric_create (-1, 1);
572 check_binary_op (qof_numeric_create (100, 100),
573 qof_numeric_mul (a, b, QOF_DENOM_AUTO,
574 QOF_HOW_DENOM_EXACT), a, b,
575 "expected %s got %s = %s * %s mult exact");
576
577 a = qof_numeric_create (2, 6);
578 b = qof_numeric_create (1, 4);
579
580 check_binary_op (qof_numeric_create (2, 24),
581 qof_numeric_mul (a, b, QOF_DENOM_AUTO,
582 QOF_HOW_DENOM_EXACT), a, b,
583 "expected %s got %s = %s * %s for mult exact");
584
585 check_binary_op (qof_numeric_create (1, 12),
586 qof_numeric_mul (a, b, QOF_DENOM_AUTO,
587 QOF_HOW_DENOM_REDUCE), a, b,
588 "expected %s got %s = %s * %s for mult reduce");
589
590 check_binary_op (qof_numeric_create (8, 100),
591 qof_numeric_mul (a, b, 100, QOF_HOW_RND_ROUND),
592 a, b, "expected %s got %s = %s * %s for mult 100th's");
593
594 check_binary_op (qof_numeric_create (8, 6),
595 qof_numeric_div (a, b, QOF_DENOM_AUTO,
596 QOF_HOW_DENOM_EXACT), a, b,
597 "expected %s got %s = %s / %s for div exact");
598
599 check_binary_op (qof_numeric_create (4, 3),
600 qof_numeric_div (a, b, QOF_DENOM_AUTO,
601 QOF_HOW_DENOM_REDUCE), a, b,
602 "expected %s got %s = %s / %s for div reduce");
603
604 check_binary_op (qof_numeric_create (133, 100),
605 qof_numeric_div (a, b, 100, QOF_HOW_RND_ROUND),
606 a, b, "expected %s got %s = %s * %s for div 100th's");
607
608 #if CHECK_ERRORS_TOO
609 QofNumeric c;
610 c = qof_numeric_mul_with_error (a, b, 100, QOF_HOW_RND_ROUND, &err);
611 printf ("mul 100ths/error : %s * %s = %s + (error) %s\n\n",
612 qof_numeric_print (a), qof_numeric_print (b),
613 qof_numeric_print (c), qof_numeric_print (err));
614
615 c = qof_numeric_div_with_error (a, b, 100, QOF_HOW_RND_ROUND, &err);
616 printf ("div 100ths/error : %s / %s = %s + (error) %s\n\n",
617 qof_numeric_print (a), qof_numeric_print (b),
618 qof_numeric_print (c), qof_numeric_print (err));
619
620 #endif
621
622 /* Check for math with 2^63 < num*num < 2^64 which previously failed
623 * see http://bugzilla.gnome.org/show_bug.cgi?id=144980
624 */
625 v = 1000000;
626 a = qof_numeric_create (1 * v, v);
627 b = qof_numeric_create (10000000 * v, v);
628
629 check_binary_op (b,
630 qof_numeric_mul (a, b, QOF_DENOM_AUTO,
631 QOF_HOW_DENOM_LCD), a, b,
632 "expected %s got %s = %s * %s for multiply");
633
634 /* Multiply some random numbers. This test presumes that
635 * RAND_MAX is approx 2^32
636 */
637 for (i = 0; i < NREPS; i++)
638 {
639 gint64 deno = 1;
640 gint64 na = rand ();
641 gint64 nb = rand ();
642 gint64 ne;
643
644 /* avoid overflow; */
645 na /= 2;
646 nb /= 2;
647 ne = na * nb;
648
649 a = qof_numeric_create (na, deno);
650 b = qof_numeric_create (nb, deno);
651
652 check_binary_op (qof_numeric_create (ne, 1),
653 qof_numeric_mul (a, b, QOF_DENOM_AUTO,
654 QOF_HOW_DENOM_EXACT), a, b,
655 "expected %s got %s = %s * %s for mult exact");
656
657 /* Force 128-bit math to come into play */
658 for (j = 1; j < 31; j++)
659 {
660 a = qof_numeric_create (na << j, 1 << j);
661 b = qof_numeric_create (nb << j, 1 << j);
662 check_binary_op (qof_numeric_create (ne, 1),
663 qof_numeric_mul (a, b, QOF_DENOM_AUTO,
664 QOF_HOW_DENOM_REDUCE), a, b,
665 "expected %s got %s = %s * %s for mult reduce");
666 }
667
668 /* Do some hokey random 128-bit division too */
669 b = qof_numeric_create (deno, nb);
670
671 check_binary_op (qof_numeric_create (ne, 1),
672 qof_numeric_div (a, b, QOF_DENOM_AUTO,
673 QOF_HOW_DENOM_EXACT), a, b,
674 "expected %s got %s = %s / %s for div exact");
675
676 /* avoid overflow; */
677 na /= 2;
678 nb /= 2;
679 ne = na * nb;
680 for (j = 1; j < 16; j++)
681 {
682 a = qof_numeric_create (na << j, 1 << j);
683 b = qof_numeric_create (1 << j, nb << j);
684 check_binary_op (qof_numeric_create (ne, 1),
685 qof_numeric_div (a, b, QOF_DENOM_AUTO,
686 QOF_HOW_DENOM_REDUCE), a, b,
687 "expected %s got %s = %s / %s for div reduce");
688 }
689 }
690
691 a = qof_numeric_create (782592055622866ULL, 89025);
692 b = qof_numeric_create (2222554708930978ULL, 85568);
693 /* Dividing the above pair overflows, in that after
694 * the division the denominator won't fit into a
695 * 64-bit quantity. This can be seen from
696 * the factorization int primes:
697 * 782592055622866 = 2 * 2283317 * 171371749
698 * (yes, thats a seven and a nine digit prime)
699 * 2222554708930978 = 2 * 1111277354465489
700 * (yes, that's a sixteen-digit prime number)
701 * 89025 = 3*5*5*1187
702 * 85568= 64*7*191
703 * If the rounding method is exact/no-round, then
704 * an overflow error should be signalled; else the
705 * divide routine should shift down the results till
706 * the overflow is eliminated.
707 *
708 */
709 check_binary_op (qof_numeric_error (QOF_ERROR_OVERFLOW),
710 qof_numeric_div (a, b, QOF_DENOM_AUTO,
711 QOF_HOW_RND_NEVER |
712 QOF_HOW_DENOM_EXACT), a, b,
713 "expected %s got %s = %s / %s for div exact");
714
715 check_binary_op (qof_numeric_create (338441, 1000000),
716 qof_numeric_div (a, b, QOF_DENOM_AUTO,
717 QOF_HOW_DENOM_SIGFIGS (6) |
718 QOF_HOW_RND_ROUND), a, b,
719 "expected %s got %s = %s / %s for div round");
720
721 /* Below is a 'typical' value calculation:
722 * value_frac = value_tot * amt_frace / amt_tot
723 * and has some typical potential-overflow values.
724 * 82718 = 2 * 59 * 701
725 * 47497125586 = 2 * 1489 * 15949337
726 * 69100955 = 5 * 7 * 11 * 179483
727 * 32005637020 = 4 * 5 * 7 * 43 * 71 * 103 * 727
728 */
729 a = qof_numeric_create (-47497125586LL, 82718);
730 b = qof_numeric_create (-69100955LL, 55739);
731 c = qof_numeric_mul (a, b, QOF_DENOM_AUTO, QOF_HOW_DENOM_EXACT);
732 d = qof_numeric_create (-32005637020LL, 55739);
733
734 check_binary_op (qof_numeric_create (-102547458LL, 82718),
735 qof_numeric_div (c, d, 82718,
736 QOF_HOW_DENOM_EXACT),
737 c, d, "expected %s got %s = %s / %s for div round");
738
739 /* If we specify QOF_HOW_RND_NEVER, then we should get an error,
740 * since the exact result won't fit into a 64-bit quantity. */
741 check_binary_op (qof_numeric_error (QOF_ERROR_REMAINDER),
742 qof_numeric_div (c, d, 82718,
743 QOF_HOW_DENOM_EXACT |
744 QOF_HOW_RND_NEVER), c, d,
745 "expected %s got %s = %s / %s for div round");
746
747 /* A simple irreducible ratio, involving negative numbers */
748 amt_a = qof_numeric_create (-6005287905LL, 40595);
749 amt_tot = qof_numeric_create (-8744187958LL, 40595);
750 frac = qof_numeric_div (amt_a, amt_tot,
751 QOF_DENOM_AUTO, QOF_HOW_DENOM_REDUCE);
752
753 check_binary_op (qof_numeric_create (6005287905LL, 8744187958LL),
754 frac, amt_a, amt_tot,
755 "expected %s got %s = %s / %s for div reduce");
756
757 /* Another overflow-prone condition */
758 val_tot = qof_numeric_create (-4280656418LL, 19873);
759 val_a = qof_numeric_mul (frac, val_tot,
760 qof_numeric_denom (val_tot),
761 QOF_HOW_RND_ROUND | QOF_HOW_DENOM_EXACT);
762 check_binary_op (qof_numeric_create (-2939846940LL, 19873),
763 val_a, val_tot, frac,
764 "expected %s got %s = %s * %s for mult round");
765
766 frac = qof_numeric_create (396226789777979LL, 328758834367851752LL);
767 val_tot = qof_numeric_create (467013515494988LL, 100);
768 val_a = qof_numeric_mul (frac, val_tot,
769 qof_numeric_denom (val_tot),
770 QOF_HOW_RND_ROUND | QOF_HOW_DENOM_EXACT);
771 check_binary_op (qof_numeric_create (562854125307LL, 100),
772 val_a, val_tot, frac,
773 "expected %s got %s = %s * %s for mult round");
774
775 /* Yet another bug from bugzilla ... */
776 a = qof_numeric_create (40066447153986554LL, 4518);
777 b = qof_numeric_create (26703286457229LL, 3192);
778 frac = qof_numeric_div (a, b,
779 QOF_DENOM_AUTO,
780 QOF_HOW_DENOM_SIGFIGS (6) | QOF_HOW_RND_ROUND);
781
782 check_binary_op (qof_numeric_create (106007, 100),
783 frac, a, b,
784 "expected %s got %s = %s / %s for mult sigfigs");
785
786 }
787
788 static void
789 check_reciprocal (void)
790 {
791 QofNumeric a, b, ans, val;
792 gdouble flo;
793
794 val = qof_numeric_create (-60, 20);
795 check_unary_op (qof_numeric_eq, qof_numeric_create (-3, -1),
796 qof_numeric_convert (val, QOF_DENOM_RECIPROCAL (1),
797 QOF_HOW_RND_NEVER),
798 val, "expected %s = %s = (%s as RECIP(1))");
799
800 a = qof_numeric_create (200, 100);
801 b = qof_numeric_create (300, 100);
802
803 /* 2 + 3 = 5 */
804 ans = qof_numeric_add (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
805 check_binary_op (qof_numeric_create (5, -1),
806 ans, a, b,
807 "expected %s got %s = %s + %s for reciprocal");
808
809 /* 2 + 3 = 5 */
810 a = qof_numeric_create (2, -1);
811 b = qof_numeric_create (300, 100);
812 ans = qof_numeric_add (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
813 check_binary_op (qof_numeric_create (5, -1),
814 ans, a, b,
815 "expected %s got %s = %s + %s for reciprocal");
816
817
818 /* 2 + 3 = 5 */
819 a = qof_numeric_create (2, -1);
820 b = qof_numeric_create (300, 100);
821 ans = qof_numeric_add (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
822 check_binary_op (qof_numeric_create (5, -1),
823 ans, a, b, "expected %s got %s = %s + %s for recirocal");
824
825 /* check gnc_numeric_to_double */
826 flo = qof_numeric_to_double (qof_numeric_create (5, -1));
827 do_test ((5.0 == flo), "reciprocal conversion");
828
829 /* check gnc_numeric_compare */
830 a = qof_numeric_create (2, 1);
831 b = qof_numeric_create (2, -1);
832 do_test ((0 == qof_numeric_compare (a, b)), " 2 == 2 ");
833 a = qof_numeric_create (2, 1);
834 b = qof_numeric_create (3, -1);
835 do_test ((-1 == qof_numeric_compare (a, b)), " 2 < 3 ");
836 a = qof_numeric_create (-2, 1);
837 b = qof_numeric_create (2, -1);
838 do_test ((-1 == qof_numeric_compare (a, b)), " -2 < 2 ");
839 a = qof_numeric_create (2, -1);
840 b = qof_numeric_create (3, -1);
841 do_test ((-1 == qof_numeric_compare (a, b)), " 2 < 3 ");
842
843 /* check for equality */
844 a = qof_numeric_create (2, 1);
845 b = qof_numeric_create (2, -1);
846 do_test (qof_numeric_equal (a, b), " 2 == 2 ");
847
848 /* check gnc_numeric_mul */
849 a = qof_numeric_create (2, 1);
850 b = qof_numeric_create (3, -1);
851 ans = qof_numeric_mul (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
852 check_binary_op (qof_numeric_create (6, -1),
853 ans, a, b, "expected %s got %s = %s * %s for recirocal");
854
855 /* check gnc_numeric_div */
856 /* -60 / 20 = -3 */
857 a = qof_numeric_create (-60, 1);
858 b = qof_numeric_create (2, -10);
859 ans = qof_numeric_div (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
860 check_binary_op (qof_numeric_create (-3, -1),
861 ans, a, b, "expected %s got %s = %s / %s for recirocal");
862
863 /* 60 / 20 = 3 */
864 a = qof_numeric_create (60, 1);
865 b = qof_numeric_create (2, -10);
866 ans = qof_numeric_div (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
867 check_binary_op (qof_numeric_create (3, -1),
868 ans, a, b, "expected %s got %s = %s / %s for recirocal");
869
870 }
871
872
873 /* ======================================================= */
874
875 static void
876 run_test (void)
877 {
878 check_eq_operator ();
879 check_reduce ();
880 check_equality_operator ();
881 check_rounding ();
882 check_double ();
883 check_neg ();
884 check_add_subtract ();
885 check_mult_div ();
886 check_reciprocal ();
887 }
888
889 int
890 main (void)
891 {
892 qof_init ();
893 run_test ();
894
895 print_test_results ();
896 exit (get_rv ());
897 qof_close ();
898 return get_rv();
899 }
900
901 /* ======================== END OF FILE ====================== */
qof_numeric_check
QofNumericErrorCode qof_numeric_check(QofNumeric in)
Definition: qofnumeric.c:44
qof_numeric_mul_with_error
QofNumeric qof_numeric_mul_with_error(QofNumeric a, QofNumeric b, gint64 denom, gint how, QofNumeric *error)
Definition: qofnumeric.c:1052
qof_numeric_mul
QofNumeric qof_numeric_mul(QofNumeric a, QofNumeric b, gint64 denom, gint how)
Definition: qofnumeric.c:405
qof_numeric_compare
gint qof_numeric_compare(QofNumeric a, QofNumeric b)
Definition: qofnumeric.c:174
qofnumeric.h
An exact-rational-number library for QOF.
qof_numeric_sub_with_error
QofNumeric qof_numeric_sub_with_error(QofNumeric a, QofNumeric b, gint64 denom, gint how, QofNumeric *error)
Definition: qofnumeric.c:1034
qof_numeric_error
QofNumeric qof_numeric_error(QofNumericErrorCode error_code)
Definition: qofnumeric.c:1004
test-engine-stuff.h
This file declares testing functions for the engine.
qof_numeric_to_double
gdouble qof_numeric_to_double(QofNumeric in)
Definition: qofnumeric.c:991
qof_numeric_equal
gboolean qof_numeric_equal(QofNumeric a, QofNumeric b)
Definition: qofnumeric.c:233
qof_numeric_div_with_error
QofNumeric qof_numeric_div_with_error(QofNumeric a, QofNumeric b, gint64 denom, gint how, QofNumeric *error)
Definition: qofnumeric.c:1071
QOF_HOW_RND_CEIL
Definition: qofnumeric.h:135
qof_numeric_add_with_error
QofNumeric qof_numeric_add_with_error(QofNumeric a, QofNumeric b, gint64 denom, gint how, QofNumeric *error)
Definition: qofnumeric.c:1014
qof_numeric_denom
static gint64 qof_numeric_denom(QofNumeric a)
Definition: qofnumeric.h:291
QOF_HOW_RND_TRUNC
Definition: qofnumeric.h:138
QOF_DENOM_RECIPROCAL
#define QOF_DENOM_RECIPROCAL(a)
Definition: qofnumeric.h:234
qof_numeric_reduce
QofNumeric qof_numeric_reduce(QofNumeric in)
Definition: qofnumeric.c:888
QOF_HOW_RND_FLOOR
Definition: qofnumeric.h:132
_QofNumeric
Definition: qofnumeric.h:52
qof_numeric_create
static QofNumeric qof_numeric_create(gint64 num, gint64 denom)
Definition: qofnumeric.h:243
QOF_HOW_RND_NEVER
Definition: qofnumeric.h:163
qof_numeric_eq
gboolean qof_numeric_eq(QofNumeric a, QofNumeric b)
Definition: qofnumeric.c:223
QOF_HOW_DENOM_EXACT
Definition: qofnumeric.h:174
qof_numeric_neg
QofNumeric qof_numeric_neg(QofNumeric a)
Definition: qofnumeric.c:637
qof_numeric_sub
QofNumeric qof_numeric_sub(QofNumeric a, QofNumeric b, gint64 denom, gint how)
Definition: qofnumeric.c:388
QOF_ERROR_REMAINDER
Definition: qofnumeric.h:218
qof_close
void qof_close(void)
Safely close down the Query Object Framework.
Definition: qofutil.c:844
qof_numeric_convert
QofNumeric qof_numeric_convert(QofNumeric in, gint64 denom, gint how)
Definition: qofnumeric.c:662
qof_numeric_add
QofNumeric qof_numeric_add(QofNumeric a, QofNumeric b, gint64 denom, gint how)
Definition: qofnumeric.c:300
qof_numeric_from_double
QofNumeric qof_numeric_from_double(gdouble in, gint64 denom, gint how)
Definition: qofnumeric.c:919
QOF_HOW_RND_ROUND
Definition: qofnumeric.h:158
QOF_HOW_DENOM_REDUCE
Definition: qofnumeric.h:181
qof_numeric_zero
static QofNumeric qof_numeric_zero(void)
Definition: qofnumeric.h:253
qof_numeric_div
QofNumeric qof_numeric_div(QofNumeric a, QofNumeric b, gint64 denom, gint how)
Definition: qofnumeric.c:516
QOF_ERROR_OVERFLOW
Definition: qofnumeric.h:211
qof_numeric_add_fixed
static QofNumeric qof_numeric_add_fixed(QofNumeric a, QofNumeric b)
Definition: qofnumeric.h:411
QOF_HOW_DENOM_LCD
Definition: qofnumeric.h:186
qof_init
void qof_init(void)
Initialise the Query Object Framework.
Definition: qofutil.c:833
QOF_DENOM_AUTO
#define QOF_DENOM_AUTO
Definition: qofnumeric.h:231
QOF_HOW_DENOM_SIGFIGS
#define QOF_HOW_DENOM_SIGFIGS(n)
Definition: qofnumeric.h:203