QOF
0.8.0
|
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 ====================== */