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

Generated on Fri May 12 18:00:33 2006 for QOF by  doxygen 1.4.4