test-numeric.c

00001 /***************************************************************************
00002  *            test-numeric.c
00003  *
00004  * Test file created by Linas Vepstas <linas@linas.org>
00005  * Review operation of the gnc-numeric tools by verifying results
00006  * of various operations.
00007  *
00008  * June 2004 
00009  *  Copyright  2004 Linas Vepstas <linas@linas.org>
00010  ****************************************************************************/
00011 /*
00012  *  This program is free software; you can redistribute it and/or modify
00013  *  it under the terms of the GNU General Public License as published by
00014  *  the Free Software Foundation; either version 2 of the License, or
00015  *  (at your option) any later version.
00016  *
00017  *  This program is distributed in the hope that it will be useful,
00018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  *  GNU General Public License for more details.
00021  *
00022  *  You should have received a copy of the GNU General Public License
00023  *  along with this program; if not, write to the Free Software
00024  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00025  *  02110-1301, USA.
00026  */
00027 
00028 #include <ctype.h>
00029 #include <glib.h>
00030 #include "qof.h"
00031 #include "test-stuff.h"
00032 #include "test-engine-stuff.h"
00033 #include "qofnumeric.h"
00034 
00035 #define NREPS 2000
00036 
00037 static char *
00038 qof_numeric_print (QofNumeric in)
00039 {
00040     char *retval;
00041     if (qof_numeric_check (in))
00042     {
00043         retval =
00044             g_strdup_printf ("<ERROR> [%" G_GINT64_FORMAT " / %"
00045                              G_GINT64_FORMAT "]", in.num, in.denom);
00046     }
00047     else
00048     {
00049         retval =
00050             g_strdup_printf ("[%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]",
00051                              in.num, in.denom);
00052     }
00053     return retval;
00054 }
00055 
00056 /* ======================================================= */
00057 
00058 static void
00059 check_unary_op (gboolean (*eqtest) (QofNumeric, QofNumeric),
00060                 QofNumeric expected,
00061                 QofNumeric actual, QofNumeric input, const gchar *errmsg)
00062 {
00063     gchar *e = qof_numeric_print (expected);
00064     gchar *r = qof_numeric_print (actual);
00065     gchar *a = qof_numeric_print (input);
00066     gchar *str = g_strdup_printf (errmsg, e, r, a);
00067 
00068     do_test (eqtest (expected, actual), str);
00069 
00070     g_free (a);
00071     g_free (r);
00072     g_free (e);
00073     g_free (str);
00074 }
00075 
00076 /* ======================================================= */
00077 
00078 static void
00079 check_binary_op (QofNumeric expected,
00080                  QofNumeric actual,
00081                  QofNumeric input_a, QofNumeric input_b, const gchar *errmsg)
00082 {
00083     gchar *e = qof_numeric_print (expected);
00084     gchar *r = qof_numeric_print (actual);
00085     gchar *a = qof_numeric_print (input_a);
00086     gchar *b = qof_numeric_print (input_b);
00087     gchar *str = g_strdup_printf (errmsg, e, r, a, b);
00088 
00089     do_test (qof_numeric_eq (expected, actual), str);
00090 
00091     g_free (a);
00092     g_free (b);
00093     g_free (r);
00094     g_free (e);
00095     g_free (str);
00096 }
00097 
00098 /* ======================================================= */
00099 
00100 static gboolean
00101 qof_numeric_unequal (QofNumeric a, QofNumeric b)
00102 {
00103     return (0 == qof_numeric_equal (a, b));
00104 }
00105 
00106 /* ======================================================= */
00107 
00108 /* Make sure that the equivalence operator we use for 
00109  * later tests actually works */
00110 static void
00111 check_eq_operator (void)
00112 {
00113     QofNumeric a = qof_numeric_create (42, 58);
00114     QofNumeric b = qof_numeric_create (42, 58);
00115     QofNumeric c = qof_numeric_create (40, 58);
00116 
00117     /* Check strict equivalence and non-equivalence */
00118     do_test (qof_numeric_eq (a, a), "expected self-equivalence");
00119     do_test (qof_numeric_eq (a, b), "expected equivalence");
00120     do_test (0 == qof_numeric_eq (a, c), "expected inequivalence");
00121 }
00122 
00123 /* ======================================================= */
00124 
00125 static void
00126 check_reduce (void)
00127 {
00128     QofNumeric one, rone;
00129     QofNumeric four, rfour;
00130     QofNumeric val, rval;
00131     /* Check common factor elimination (needed for equality checks) */
00132     one = qof_numeric_create (1, 1);
00133     rone = qof_numeric_create (1000000, 1000000);
00134     rone = qof_numeric_reduce (rone);
00135     do_test (qof_numeric_eq (one, rone), "reduce to one");
00136 
00137     four = qof_numeric_create (4, 1);
00138     rfour = qof_numeric_create (480, 120);
00139     rfour = qof_numeric_reduce (rfour);
00140     do_test (qof_numeric_eq (four, rfour), "reduce to four");
00141 
00142     val = qof_numeric_create (10023234LL, 334216654LL);
00143     rval = qof_numeric_reduce (val);
00144     check_unary_op (qof_numeric_eq,
00145                     qof_numeric_create (5011617, 167108327),
00146                     rval,
00147                     val, "check_reduce(1) expected %s = %s = reduce(%s)");
00148 
00149     val = qof_numeric_create (17474724864LL, 136048896LL);
00150     rval = qof_numeric_reduce (val);
00151     check_unary_op (qof_numeric_eq,
00152                     qof_numeric_create (4 * 17 * 17, 9),
00153                     rval,
00154                     val, "check_reduce(2) expected %s = %s = reduce(%s)");
00155 
00156     val = qof_numeric_create (1024LL, 1099511627776LL);
00157     rval = qof_numeric_reduce (val);
00158     check_unary_op (qof_numeric_eq,
00159                     qof_numeric_create (1, 1024 * 1024 * 1024),
00160                     rval,
00161                     val, "check_reduce(3): expected %s = %s = reduce(%s)");
00162 }
00163 
00164 /* ======================================================= */
00165 
00166 static void
00167 check_equality_operator (void)
00168 {
00169     gint i, m;
00170     gint mult;
00171     gint64 f, deno, numer;
00172     QofNumeric big, rbig;
00173     QofNumeric val, mval;
00174     QofNumeric bval, rval;
00175     /* Check equality operator for some large numer/denom values */
00176     numer = 1 << 30;
00177     numer <<= 30;               /* we don't trust cpp to compute 1<<60 correctly */
00178     deno = 1 << 30;
00179     deno <<= 20;
00180     rbig = qof_numeric_create (numer, deno);
00181 
00182     big = qof_numeric_create (1 << 10, 1);
00183     do_test (qof_numeric_equal (big, rbig), "equal to billion");
00184 
00185     big = qof_numeric_create (1 << 20, 1 << 10);
00186     do_test (qof_numeric_equal (big, rbig), "equal to 1<<20/1<<10");
00187 
00188     big = qof_numeric_create (1 << 30, 1 << 20);
00189     do_test (qof_numeric_equal (big, rbig), "equal to 1<<30/1<<20");
00190 
00191     numer = 1 << 30;
00192     numer <<= 30;               /* we don't trust cpp to compute 1<<60 correctly */
00193     deno = 1 << 30;
00194     rbig = qof_numeric_create (numer, deno);
00195 
00196     big = qof_numeric_create (1 << 30, 1);
00197     do_test (qof_numeric_equal (big, rbig), "equal to 1<<30");
00198 
00199     numer = 1 << 30;
00200     numer <<= 10;
00201     big = qof_numeric_create (numer, 1 << 10);
00202     do_test (qof_numeric_equal (big, rbig), "equal to 1<<40/1<<10");
00203 
00204     numer <<= 10;
00205     big = qof_numeric_create (numer, 1 << 20);
00206     do_test (qof_numeric_equal (big, rbig), "equal to 1<<50/1<<20");
00207 
00208     /* We assume RAND_MAX is less that 1<<32 */
00209     for (i = 0; i < NREPS; i++)
00210     {
00211         deno = rand () / 2;
00212         mult = rand () / 2;
00213         numer = rand () / 2;
00214 
00215         val = qof_numeric_create (numer, deno);
00216         mval = qof_numeric_create (numer * mult, deno * mult);
00217 
00218         /* The reduced version should be equivalent */
00219         bval = qof_numeric_reduce (val);
00220         rval = qof_numeric_reduce (mval);
00221         check_unary_op (qof_numeric_eq,
00222                         bval, rval, mval, "expected %s = %s = reduce(%s)");
00223 
00224         /* The unreduced versions should be equal */
00225         check_unary_op (qof_numeric_equal,
00226                         val, mval, mval, "expected %s = %s");
00227 
00228         /* Certain modulo's should be very cleary un-equal; this
00229          * helps stop funky modulo-64 aliasing in compares that 
00230          * might creep in. */
00231         mval.denom >>= 1;
00232         mval.num >>= 1;
00233         m = 0;
00234         f = mval.denom;
00235         while (f % 2 == 0)
00236         {
00237             f >>= 1;
00238             m++;
00239         }
00240         if (1 < m)
00241         {
00242             gint64 nn = 1 << (32 - m);
00243             nn <<= 32;
00244             nn += mval.num;
00245             val = qof_numeric_create (2 * nn, 2 * mval.denom);
00246             check_unary_op (qof_numeric_unequal,
00247                             val, mval, mval, "expected unequality %s != %s");
00248 
00249         }
00250     }
00251 }
00252 
00253 /* ======================================================= */
00254 
00255 static void
00256 check_rounding (void)
00257 {
00258     QofNumeric val;
00259 
00260     val = qof_numeric_create (7, 16);
00261     check_unary_op (qof_numeric_eq,
00262                     qof_numeric_create (43, 100),
00263                     qof_numeric_convert (val, 100, QOF_HOW_RND_FLOOR),
00264                     val, "expected %s = %s = (%s as 100th's floor)");
00265     check_unary_op (qof_numeric_eq,
00266                     qof_numeric_create (44, 100),
00267                     qof_numeric_convert (val, 100, QOF_HOW_RND_CEIL),
00268                     val, "expected %s = %s = (%s as 100th's ceiling)");
00269     check_unary_op (qof_numeric_eq,
00270                     qof_numeric_create (43, 100),
00271                     qof_numeric_convert (val, 100, QOF_HOW_RND_TRUNC),
00272                     val, "expected %s = %s = (%s as 100th's trunc)");
00273     check_unary_op (qof_numeric_eq,
00274                     qof_numeric_create (44, 100),
00275                     qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
00276                     val, "expected %s = %s = (%s as 100th's round)");
00277 
00278     val = qof_numeric_create (1511, 1000);
00279     check_unary_op (qof_numeric_eq,
00280                     qof_numeric_create (151, 100),
00281                     qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
00282                     val, "expected %s = %s = (%s as 100th's round)");
00283 
00284     val = qof_numeric_create (1516, 1000);
00285     check_unary_op (qof_numeric_eq,
00286                     qof_numeric_create (152, 100),
00287                     qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
00288                     val, "expected %s = %s = (%s as 100th's round)");
00289 
00290     /* Half-values always get rounded to nearest even number */
00291     val = qof_numeric_create (1515, 1000);
00292     check_unary_op (qof_numeric_eq,
00293                     qof_numeric_create (152, 100),
00294                     qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
00295                     val, "expected %s = %s = (%s as 100th's round)");
00296 
00297     val = qof_numeric_create (1525, 1000);
00298     check_unary_op (qof_numeric_eq,
00299                     qof_numeric_create (152, 100),
00300                     qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
00301                     val, "expected %s = %s = (%s as 100th's round)");
00302 
00303     val = qof_numeric_create (1535, 1000);
00304     check_unary_op (qof_numeric_eq,
00305                     qof_numeric_create (154, 100),
00306                     qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
00307                     val, "expected %s = %s = (%s as 100th's round)");
00308 
00309     val = qof_numeric_create (1545, 1000);
00310     check_unary_op (qof_numeric_eq,
00311                     qof_numeric_create (154, 100),
00312                     qof_numeric_convert (val, 100, QOF_HOW_RND_ROUND),
00313                     val, "expected %s = %s = (%s as 100th's round)");
00314 }
00315 
00316 /* ======================================================= */
00317 
00318 static void
00319 check_double (void)
00320 {
00321     gdouble flo;
00322     QofNumeric val = qof_numeric_create (0, 1);
00323 
00324     check_unary_op (qof_numeric_eq,
00325                     qof_numeric_create (112346, 100000),
00326                     qof_numeric_from_double (1.1234567890123,
00327                                            QOF_DENOM_AUTO,
00328                                            QOF_HOW_DENOM_SIGFIGS (6) |
00329                                            QOF_HOW_RND_ROUND),
00330                     val, "expected %s = %s double 6 figs");
00331 
00332     check_unary_op (qof_numeric_eq,
00333                     qof_numeric_create (112346, 10000000),
00334                     qof_numeric_from_double (0.011234567890123,
00335                                            QOF_DENOM_AUTO,
00336                                            QOF_HOW_DENOM_SIGFIGS (6) |
00337                                            QOF_HOW_RND_ROUND),
00338                     val, "expected %s = %s double 6 figs");
00339 
00340     check_unary_op (qof_numeric_eq,
00341                     qof_numeric_create (112346, 100),
00342                     qof_numeric_from_double (1123.4567890123,
00343                                            QOF_DENOM_AUTO,
00344                                            QOF_HOW_DENOM_SIGFIGS (6) |
00345                                            QOF_HOW_RND_ROUND),
00346                     val, "expected %s = %s double 6 figs");
00347     check_unary_op (qof_numeric_eq,
00348                     qof_numeric_create (112346, 10000000000LL),
00349                     qof_numeric_from_double (1.1234567890123e-5,
00350                                            QOF_DENOM_AUTO,
00351                                            QOF_HOW_DENOM_SIGFIGS (6) |
00352                                            QOF_HOW_RND_ROUND),
00353                     val, "expected %s = %s double 6 figs");
00354 
00355     flo = qof_numeric_to_double (qof_numeric_create (7, 16));
00356     do_test ((0.4375 == flo), "float pt conversion");
00357 }
00358 
00359 /* ======================================================= */
00360 
00361 static void
00362 check_neg (void)
00363 {
00364     QofNumeric a = qof_numeric_create (2, 6);
00365     QofNumeric b = qof_numeric_create (1, 4);
00366     QofNumeric c = qof_numeric_neg (a);
00367     QofNumeric d = qof_numeric_neg (b);
00368 
00369     check_unary_op (qof_numeric_eq,
00370                     qof_numeric_create (-2, 6), c,
00371                     a, "expected %s = %s = -(%s)");
00372 
00373     check_unary_op (qof_numeric_eq,
00374                     qof_numeric_create (-1, 4), d,
00375                     b, "expected %s = %s = -(%s)");
00376 
00377 }
00378 
00379 /* ======================================================= */
00380 
00381 static void
00382 check_add_subtract (void)
00383 {
00384     gint i;
00385     QofNumeric a, b, c, d, z;
00386 #if CHECK_ERRORS_TOO
00387     QofNumeric c;
00388 #endif
00389 
00390     a = qof_numeric_create (2, 6);
00391     b = qof_numeric_create (1, 4);
00392 
00393     /* Well, actually 14/24 would be acceptable/better in this case */
00394     check_binary_op (qof_numeric_create (7, 12),
00395                      qof_numeric_add (a, b, QOF_DENOM_AUTO,
00396                                       QOF_HOW_DENOM_EXACT), a, b,
00397                      "expected %s got %s = %s + %s for add exact");
00398 
00399     check_binary_op (qof_numeric_create (58, 100),
00400                      qof_numeric_add (a, b, 100, QOF_HOW_RND_ROUND),
00401                      a, b,
00402                      "expected %s got %s = %s + %s for add 100ths (banker's)");
00403 
00404     check_binary_op (qof_numeric_create (5833, 10000),
00405                      qof_numeric_add (a, b, QOF_DENOM_AUTO,
00406                                       QOF_HOW_DENOM_SIGFIGS (4) |
00407                                       QOF_HOW_RND_ROUND),
00408                      a, b, "expected %s got %s = %s + %s for add 4 sig figs");
00409 
00410     check_binary_op (qof_numeric_create (583333, 1000000),
00411                      qof_numeric_add (a, b, QOF_DENOM_AUTO,
00412                                       QOF_HOW_DENOM_SIGFIGS (6) |
00413                                       QOF_HOW_RND_ROUND),
00414                      a, b, "expected %s got %s = %s + %s for add 6 sig figs");
00415 
00416     check_binary_op (qof_numeric_create (1, 12),
00417                      qof_numeric_sub (a, b, QOF_DENOM_AUTO,
00418                                       QOF_HOW_DENOM_EXACT), a, b,
00419                      "expected %s got %s = %s - %s for sub exact");
00420 
00421     /* We should try something trickier for reduce & lcd */
00422     check_binary_op (qof_numeric_create (1, 12),
00423                      qof_numeric_sub (a, b, QOF_DENOM_AUTO,
00424                                       QOF_HOW_DENOM_REDUCE), a, b,
00425                      "expected %s got %s = %s - %s for sub reduce");
00426 
00427     check_binary_op (qof_numeric_create (1, 12),
00428                      qof_numeric_sub (a, b, QOF_DENOM_AUTO,
00429                                       QOF_HOW_DENOM_LCD), a, b,
00430                      "expected %s got %s = %s - %s for sub reduce");
00431 
00432     check_binary_op (qof_numeric_create (8, 100),
00433                      qof_numeric_sub (a, b, 100, QOF_HOW_RND_ROUND),
00434                      a, b,
00435                      "expected %s got %s = %s - %s for sub 100ths (banker's)");
00436 
00437     /* ------------------------------------------------------------ */
00438     /* This test has failed before */
00439     c = qof_numeric_neg (a);
00440     d = qof_numeric_neg (b);
00441     z = qof_numeric_zero ();
00442     check_binary_op (c, qof_numeric_add_fixed (z, c),
00443                      z, c, "expected %s got %s = %s + %s for add fixed");
00444 
00445     check_binary_op (d, qof_numeric_add_fixed (z, d),
00446                      z, d, "expected %s got %s = %s + %s for add fixed");
00447 
00448     /* ------------------------------------------------------------ */
00449     /* Same as above, but with signs reviersed */
00450     a = c;
00451     b = d;
00452     /* Well, actually 14/24 would be acceptable/better in this case */
00453     check_binary_op (qof_numeric_create (-7, 12),
00454                      qof_numeric_add (a, b, QOF_DENOM_AUTO,
00455                                       QOF_HOW_DENOM_EXACT), a, b,
00456                      "expected %s got %s = %s + %s for add exact");
00457 
00458     check_binary_op (qof_numeric_create (-58, 100),
00459                      qof_numeric_add (a, b, 100, QOF_HOW_RND_ROUND),
00460                      a, b,
00461                      "expected %s got %s = %s + %s for add 100ths (banker's)");
00462 
00463     check_binary_op (qof_numeric_create (-5833, 10000),
00464                      qof_numeric_add (a, b, QOF_DENOM_AUTO,
00465                                       QOF_HOW_DENOM_SIGFIGS (4) |
00466                                       QOF_HOW_RND_ROUND),
00467                      a, b, "expected %s got %s = %s + %s for add 4 sig figs");
00468 
00469     check_binary_op (qof_numeric_create (-583333, 1000000),
00470                      qof_numeric_add (a, b, QOF_DENOM_AUTO,
00471                                       QOF_HOW_DENOM_SIGFIGS (6) |
00472                                       QOF_HOW_RND_ROUND),
00473                      a, b, "expected %s got %s = %s + %s for add 6 sig figs");
00474 
00475     check_binary_op (qof_numeric_create (-1, 12),
00476                      qof_numeric_sub (a, b, QOF_DENOM_AUTO,
00477                                       QOF_HOW_DENOM_EXACT), a, b,
00478                      "expected %s got %s = %s - %s for sub exact");
00479 
00480     /* We should try something trickier for reduce & lcd */
00481     check_binary_op (qof_numeric_create (-1, 12),
00482                      qof_numeric_sub (a, b, QOF_DENOM_AUTO,
00483                                       QOF_HOW_DENOM_REDUCE), a, b,
00484                      "expected %s got %s = %s - %s for sub reduce");
00485 
00486     check_binary_op (qof_numeric_create (-1, 12),
00487                      qof_numeric_sub (a, b, QOF_DENOM_AUTO,
00488                                       QOF_HOW_DENOM_LCD), a, b,
00489                      "expected %s got %s = %s - %s for sub reduce");
00490 
00491     check_binary_op (qof_numeric_create (-8, 100),
00492                      qof_numeric_sub (a, b, 100, QOF_HOW_RND_ROUND),
00493                      a, b,
00494                      "expected %s got %s = %s - %s for sub 100ths (banker's)");
00495 
00496     /* ------------------------------------------------------------ */
00497 #if CHECK_ERRORS_TOO
00498     c = qof_numeric_add_with_error (a, b, 100, QOF_HOW_RND_ROUND, &err);
00499     printf ("add 100ths/error : %s + %s = %s + (error) %s\n\n",
00500             qof_numeric_print (a), qof_numeric_print (b),
00501             qof_numeric_print (c), qof_numeric_print (err));
00502 
00503     c = qof_numeric_sub_with_error (a, b, 100, QOf_HOW_RND_FLOOR, &err);
00504     printf ("sub 100ths/error : %s - %s = %s + (error) %s\n\n",
00505             qof_numeric_print (a), qof_numeric_print (b),
00506             qof_numeric_print (c), qof_numeric_print (err));
00507 
00508 #endif
00509 
00510     /* ------------------------------------------------------------ */
00511     /* Add and subtract some random numbers */
00512     for (i = 0; i < NREPS; i++)
00513     {
00514         QofNumeric e;
00515         gint64 deno = rand () + 1;
00516         gint64 na = get_random_gint64 ();
00517         gint64 nb = get_random_gint64 ();
00518         gint64 ne;
00519 
00520         /* avoid overflow; */
00521         na /= 2;
00522         nb /= 2;
00523 
00524         a = qof_numeric_create (na, deno);
00525         b = qof_numeric_create (nb, deno);
00526 
00527         /* Add */
00528         ne = na + nb;
00529         e = qof_numeric_create (ne, deno);
00530         check_binary_op (e,
00531                          qof_numeric_add (a, b, QOF_DENOM_AUTO,
00532                                           QOF_HOW_DENOM_EXACT), a, b,
00533                          "expected %s got %s = %s + %s for exact addition");
00534 
00535         /* Subtract */
00536         ne = na - nb;
00537         e = qof_numeric_create (ne, deno);
00538         check_binary_op (e,
00539                          qof_numeric_sub (a, b, QOF_DENOM_AUTO,
00540                                           QOF_HOW_DENOM_EXACT), a, b,
00541                          "expected %s got %s = %s - %s for exact subtraction");
00542     }
00543 }
00544 
00545 /* ======================================================= */
00546 
00547 static void
00548 check_mult_div (void)
00549 {
00550     gint i, j;
00551     gint64 v;
00552     QofNumeric c, d;
00553     QofNumeric amt_a, amt_tot, frac, val_tot, val_a;
00554     QofNumeric a, b;
00555 
00556     a = qof_numeric_create (-100, 100);
00557     b = qof_numeric_create (1, 1);
00558     check_binary_op (qof_numeric_create (-100, 100),
00559                      qof_numeric_div (a, b, QOF_DENOM_AUTO,
00560                                       QOF_HOW_DENOM_EXACT), a, b,
00561                      "expected %s got %s = %s / %s div exact");
00562 
00563     a = qof_numeric_create (-100, 100);
00564     b = qof_numeric_create (-1, 1);
00565     check_binary_op (qof_numeric_create (100, 100),
00566                      qof_numeric_div (a, b, QOF_DENOM_AUTO,
00567                                       QOF_HOW_DENOM_EXACT), a, b,
00568                      "expected %s got %s = %s / %s div exact");
00569 
00570     a = qof_numeric_create (-100, 100);
00571     b = qof_numeric_create (-1, 1);
00572     check_binary_op (qof_numeric_create (100, 100),
00573                      qof_numeric_mul (a, b, QOF_DENOM_AUTO,
00574                                       QOF_HOW_DENOM_EXACT), a, b,
00575                      "expected %s got %s = %s * %s mult exact");
00576 
00577     a = qof_numeric_create (2, 6);
00578     b = qof_numeric_create (1, 4);
00579 
00580     check_binary_op (qof_numeric_create (2, 24),
00581                      qof_numeric_mul (a, b, QOF_DENOM_AUTO,
00582                                       QOF_HOW_DENOM_EXACT), a, b,
00583                      "expected %s got %s = %s * %s for mult exact");
00584 
00585     check_binary_op (qof_numeric_create (1, 12),
00586                      qof_numeric_mul (a, b, QOF_DENOM_AUTO,
00587                                       QOF_HOW_DENOM_REDUCE), a, b,
00588                      "expected %s got %s = %s * %s for mult reduce");
00589 
00590     check_binary_op (qof_numeric_create (8, 100),
00591                      qof_numeric_mul (a, b, 100, QOF_HOW_RND_ROUND),
00592                      a, b, "expected %s got %s = %s * %s for mult 100th's");
00593 
00594     check_binary_op (qof_numeric_create (8, 6),
00595                      qof_numeric_div (a, b, QOF_DENOM_AUTO,
00596                                       QOF_HOW_DENOM_EXACT), a, b,
00597                      "expected %s got %s = %s / %s for div exact");
00598 
00599     check_binary_op (qof_numeric_create (4, 3),
00600                      qof_numeric_div (a, b, QOF_DENOM_AUTO,
00601                                       QOF_HOW_DENOM_REDUCE), a, b,
00602                      "expected %s got %s = %s / %s for div reduce");
00603 
00604     check_binary_op (qof_numeric_create (133, 100),
00605                      qof_numeric_div (a, b, 100, QOF_HOW_RND_ROUND),
00606                      a, b, "expected %s got %s = %s * %s for div 100th's");
00607 
00608 #if CHECK_ERRORS_TOO
00609     QofNumeric c;
00610     c = qof_numeric_mul_with_error (a, b, 100, QOF_HOW_RND_ROUND, &err);
00611     printf ("mul 100ths/error : %s * %s = %s + (error) %s\n\n",
00612             qof_numeric_print (a), qof_numeric_print (b),
00613             qof_numeric_print (c), qof_numeric_print (err));
00614 
00615     c = qof_numeric_div_with_error (a, b, 100, QOF_HOW_RND_ROUND, &err);
00616     printf ("div 100ths/error : %s / %s = %s + (error) %s\n\n",
00617             qof_numeric_print (a), qof_numeric_print (b),
00618             qof_numeric_print (c), qof_numeric_print (err));
00619 
00620 #endif
00621 
00622     /* Check for math with 2^63 < num*num < 2^64 which previously failed 
00623      * see http://bugzilla.gnome.org/show_bug.cgi?id=144980 
00624      */
00625     v = 1000000;
00626     a = qof_numeric_create (1 * v, v);
00627     b = qof_numeric_create (10000000 * v, v);
00628 
00629     check_binary_op (b,
00630                      qof_numeric_mul (a, b, QOF_DENOM_AUTO,
00631                                       QOF_HOW_DENOM_LCD), a, b,
00632                      "expected %s got %s = %s * %s for multiply");
00633 
00634     /* Multiply some random numbers.  This test presumes that
00635      * RAND_MAX is approx 2^32 
00636      */
00637     for (i = 0; i < NREPS; i++)
00638     {
00639         gint64 deno = 1;
00640         gint64 na = rand ();
00641         gint64 nb = rand ();
00642         gint64 ne;
00643 
00644         /* avoid overflow; */
00645         na /= 2;
00646         nb /= 2;
00647         ne = na * nb;
00648 
00649         a = qof_numeric_create (na, deno);
00650         b = qof_numeric_create (nb, deno);
00651 
00652         check_binary_op (qof_numeric_create (ne, 1),
00653                          qof_numeric_mul (a, b, QOF_DENOM_AUTO,
00654                                           QOF_HOW_DENOM_EXACT), a, b,
00655                          "expected %s got %s = %s * %s for mult exact");
00656 
00657         /* Force 128-bit math to come into play */
00658         for (j = 1; j < 31; j++)
00659         {
00660             a = qof_numeric_create (na << j, 1 << j);
00661             b = qof_numeric_create (nb << j, 1 << j);
00662             check_binary_op (qof_numeric_create (ne, 1),
00663                              qof_numeric_mul (a, b, QOF_DENOM_AUTO,
00664                                               QOF_HOW_DENOM_REDUCE), a, b,
00665                              "expected %s got %s = %s * %s for mult reduce");
00666         }
00667 
00668         /* Do some hokey random 128-bit division too */
00669         b = qof_numeric_create (deno, nb);
00670 
00671         check_binary_op (qof_numeric_create (ne, 1),
00672                          qof_numeric_div (a, b, QOF_DENOM_AUTO,
00673                                           QOF_HOW_DENOM_EXACT), a, b,
00674                          "expected %s got %s = %s / %s for div exact");
00675 
00676         /* avoid overflow; */
00677         na /= 2;
00678         nb /= 2;
00679         ne = na * nb;
00680         for (j = 1; j < 16; j++)
00681         {
00682             a = qof_numeric_create (na << j, 1 << j);
00683             b = qof_numeric_create (1 << j, nb << j);
00684             check_binary_op (qof_numeric_create (ne, 1),
00685                              qof_numeric_div (a, b, QOF_DENOM_AUTO,
00686                                               QOF_HOW_DENOM_REDUCE), a, b,
00687                              "expected %s got %s = %s / %s for div reduce");
00688         }
00689     }
00690 
00691     a = qof_numeric_create (782592055622866ULL, 89025);
00692     b = qof_numeric_create (2222554708930978ULL, 85568);
00693     /* Dividing the above pair overflows, in that after
00694      * the division the denominator won't fit into a 
00695      * 64-bit quantity.  This can be seen from
00696      * the factorization int primes:
00697      * 782592055622866 = 2 * 2283317 * 171371749
00698      * (yes, thats a seven and a nine digit prime)
00699      * 2222554708930978 = 2 * 1111277354465489
00700      * (yes, that's a sixteen-digit prime number)
00701      * 89025 = 3*5*5*1187 
00702      * 85568= 64*7*191   
00703      * If the rounding method is exact/no-round, then 
00704      * an overflow error should be signalled; else the 
00705      * divide routine should shift down the results till
00706      * the overflow is eliminated.
00707      * 
00708      */
00709     check_binary_op (qof_numeric_error (QOF_ERROR_OVERFLOW),
00710                      qof_numeric_div (a, b, QOF_DENOM_AUTO,
00711                                       QOF_HOW_RND_NEVER |
00712                                       QOF_HOW_DENOM_EXACT), a, b,
00713                      "expected %s got %s = %s / %s for div exact");
00714 
00715     check_binary_op (qof_numeric_create (338441, 1000000),
00716                      qof_numeric_div (a, b, QOF_DENOM_AUTO,
00717                                       QOF_HOW_DENOM_SIGFIGS (6) |
00718                                       QOF_HOW_RND_ROUND), a, b,
00719                      "expected %s got %s = %s / %s for div round");
00720 
00721     /* Below is a 'typical' value calculation: 
00722      * value_frac = value_tot * amt_frace / amt_tot
00723      * and has some typical potential-overflow values. 
00724      * 82718 = 2 * 59 * 701
00725      * 47497125586 = 2 * 1489 * 15949337
00726      * 69100955 = 5 * 7 * 11 * 179483
00727      * 32005637020 = 4 * 5 * 7 * 43 * 71 * 103 * 727
00728      */
00729     a = qof_numeric_create (-47497125586LL, 82718);
00730     b = qof_numeric_create (-69100955LL, 55739);
00731     c = qof_numeric_mul (a, b, QOF_DENOM_AUTO, QOF_HOW_DENOM_EXACT);
00732     d = qof_numeric_create (-32005637020LL, 55739);
00733 
00734     check_binary_op (qof_numeric_create (-102547458LL, 82718),
00735                      qof_numeric_div (c, d, 82718,
00736                                       QOF_HOW_DENOM_EXACT),
00737                      c, d, "expected %s got %s = %s / %s for div round");
00738 
00739     /* If we specify QOF_HOW_RND_NEVER, then we should get an error,
00740      * since the exact result won't fit into a 64-bit quantity. */
00741     check_binary_op (qof_numeric_error (QOF_ERROR_REMAINDER),
00742                      qof_numeric_div (c, d, 82718,
00743                                       QOF_HOW_DENOM_EXACT |
00744                                       QOF_HOW_RND_NEVER), c, d,
00745                      "expected %s got %s = %s / %s for div round");
00746 
00747     /* A simple irreducible ratio, involving negative numbers */
00748     amt_a = qof_numeric_create (-6005287905LL, 40595);
00749     amt_tot = qof_numeric_create (-8744187958LL, 40595);
00750     frac = qof_numeric_div (amt_a, amt_tot,
00751                             QOF_DENOM_AUTO, QOF_HOW_DENOM_REDUCE);
00752 
00753     check_binary_op (qof_numeric_create (6005287905LL, 8744187958LL),
00754                      frac, amt_a, amt_tot,
00755                      "expected %s got %s = %s / %s for div reduce");
00756 
00757     /* Another overflow-prone condition */
00758     val_tot = qof_numeric_create (-4280656418LL, 19873);
00759     val_a = qof_numeric_mul (frac, val_tot,
00760                              qof_numeric_denom (val_tot),
00761                              QOF_HOW_RND_ROUND | QOF_HOW_DENOM_EXACT);
00762     check_binary_op (qof_numeric_create (-2939846940LL, 19873),
00763                      val_a, val_tot, frac,
00764                      "expected %s got %s = %s * %s for mult round");
00765 
00766     frac = qof_numeric_create (396226789777979LL, 328758834367851752LL);
00767     val_tot = qof_numeric_create (467013515494988LL, 100);
00768     val_a = qof_numeric_mul (frac, val_tot,
00769                              qof_numeric_denom (val_tot),
00770                              QOF_HOW_RND_ROUND | QOF_HOW_DENOM_EXACT);
00771     check_binary_op (qof_numeric_create (562854125307LL, 100),
00772                      val_a, val_tot, frac,
00773                      "expected %s got %s = %s * %s for mult round");
00774 
00775     /* Yet another bug from bugzilla ... */
00776     a = qof_numeric_create (40066447153986554LL, 4518);
00777     b = qof_numeric_create (26703286457229LL, 3192);
00778     frac = qof_numeric_div (a, b,
00779                             QOF_DENOM_AUTO,
00780                             QOF_HOW_DENOM_SIGFIGS (6) | QOF_HOW_RND_ROUND);
00781 
00782     check_binary_op (qof_numeric_create (106007, 100),
00783                      frac, a, b,
00784                      "expected %s got %s = %s / %s for mult sigfigs");
00785 
00786 }
00787 
00788 static void
00789 check_reciprocal (void)
00790 {
00791     QofNumeric a, b, ans, val;
00792     gdouble flo;
00793 
00794     val = qof_numeric_create (-60, 20);
00795     check_unary_op (qof_numeric_eq, qof_numeric_create (-3, -1),
00796                     qof_numeric_convert (val, QOF_DENOM_RECIPROCAL (1),
00797                                          QOF_HOW_RND_NEVER),
00798                     val, "expected %s = %s = (%s as RECIP(1))");
00799 
00800     a = qof_numeric_create (200, 100);
00801     b = qof_numeric_create (300, 100);
00802 
00803     /* 2 + 3 = 5 */
00804     ans = qof_numeric_add (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
00805     check_binary_op (qof_numeric_create (5, -1),
00806                      ans, a, b,
00807                      "expected %s got %s = %s + %s for reciprocal");
00808 
00809     /* 2 + 3 = 5 */
00810     a = qof_numeric_create (2, -1);
00811     b = qof_numeric_create (300, 100);
00812     ans = qof_numeric_add (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
00813     check_binary_op (qof_numeric_create (5, -1),
00814                      ans, a, b,
00815                      "expected %s got %s = %s + %s for reciprocal");
00816 
00817 
00818     /* 2 + 3 = 5 */
00819     a = qof_numeric_create (2, -1);
00820     b = qof_numeric_create (300, 100);
00821     ans = qof_numeric_add (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
00822     check_binary_op (qof_numeric_create (5, -1),
00823                      ans, a, b, "expected %s got %s = %s + %s for recirocal");
00824 
00825     /* check gnc_numeric_to_double */
00826     flo = qof_numeric_to_double (qof_numeric_create (5, -1));
00827     do_test ((5.0 == flo), "reciprocal conversion");
00828 
00829     /* check gnc_numeric_compare */
00830     a = qof_numeric_create (2, 1);
00831     b = qof_numeric_create (2, -1);
00832     do_test ((0 == qof_numeric_compare (a, b)), " 2 == 2 ");
00833     a = qof_numeric_create (2, 1);
00834     b = qof_numeric_create (3, -1);
00835     do_test ((-1 == qof_numeric_compare (a, b)), " 2 < 3 ");
00836     a = qof_numeric_create (-2, 1);
00837     b = qof_numeric_create (2, -1);
00838     do_test ((-1 == qof_numeric_compare (a, b)), " -2 < 2 ");
00839     a = qof_numeric_create (2, -1);
00840     b = qof_numeric_create (3, -1);
00841     do_test ((-1 == qof_numeric_compare (a, b)), " 2 < 3 ");
00842 
00843     /* check for equality */
00844     a = qof_numeric_create (2, 1);
00845     b = qof_numeric_create (2, -1);
00846     do_test (qof_numeric_equal (a, b), " 2 == 2 ");
00847 
00848     /* check gnc_numeric_mul */
00849     a = qof_numeric_create (2, 1);
00850     b = qof_numeric_create (3, -1);
00851     ans = qof_numeric_mul (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
00852     check_binary_op (qof_numeric_create (6, -1),
00853                      ans, a, b, "expected %s got %s = %s * %s for recirocal");
00854 
00855     /* check gnc_numeric_div */
00856     /* -60 / 20 = -3 */
00857     a = qof_numeric_create (-60, 1);
00858     b = qof_numeric_create (2, -10);
00859     ans = qof_numeric_div (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
00860     check_binary_op (qof_numeric_create (-3, -1),
00861                      ans, a, b, "expected %s got %s = %s / %s for recirocal");
00862 
00863     /* 60 / 20 = 3 */
00864     a = qof_numeric_create (60, 1);
00865     b = qof_numeric_create (2, -10);
00866     ans = qof_numeric_div (a, b, QOF_DENOM_RECIPROCAL (1), QOF_HOW_RND_NEVER);
00867     check_binary_op (qof_numeric_create (3, -1),
00868                      ans, a, b, "expected %s got %s = %s / %s for recirocal");
00869 
00870 }
00871 
00872 
00873 /* ======================================================= */
00874 
00875 static void
00876 run_test (void)
00877 {
00878     check_eq_operator ();
00879     check_reduce ();
00880     check_equality_operator ();
00881     check_rounding ();
00882     check_double ();
00883     check_neg ();
00884     check_add_subtract ();
00885     check_mult_div ();
00886     check_reciprocal ();
00887 }
00888 
00889 int
00890 main (void)
00891 {
00892     qof_init ();
00893     run_test ();
00894 
00895     print_test_results ();
00896     exit (get_rv ());
00897     qof_close ();
00898     return get_rv();
00899 }
00900 
00901 /* ======================== END OF FILE ====================== */

Generated on Thu Jan 31 22:50:26 2008 for QOF by  doxygen 1.5.4