|
GnuCash 2.4.99
|
00001 /******************************************************************** 00002 * qofmath128.c -- an 128-bit integer library * 00003 * Copyright (C) 2004 Linas Vepstas <linas@linas.org> * 00004 * * 00005 * This program is free software; you can redistribute it and/or * 00006 * modify it under the terms of the GNU General Public License as * 00007 * published by the Free Software Foundation; either version 2 of * 00008 * the License, or (at your option) any later version. * 00009 * * 00010 * This program is distributed in the hope that it will be useful, * 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00013 * GNU General Public License for more details. * 00014 * * 00015 * You should have received a copy of the GNU General Public License* 00016 * along with this program; if not, contact: * 00017 * * 00018 * Free Software Foundation Voice: +1-617-542-5942 * 00019 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00020 * Boston, MA 02110-1301, USA gnu@gnu.org * 00021 * * 00022 *******************************************************************/ 00023 00024 #include "config.h" 00025 #include "qofmath128-p.h" 00026 00027 #include <glib.h> 00028 00029 /* =============================================================== */ 00030 /* 00031 * Quick-n-dirty 128-bit integer math lib. Things seem to mostly 00032 * work, and have been tested, but not comprehensively tested. 00033 */ 00034 00035 #define HIBIT (0x8000000000000000ULL) 00036 00040 qofint128 00041 mult128 (gint64 a, gint64 b) 00042 { 00043 qofint128 prod; 00044 guint64 a0, a1; 00045 guint64 b0, b1; 00046 guint64 d, d0, d1; 00047 guint64 e, e0, e1; 00048 guint64 f, f0, f1; 00049 guint64 g, g0, g1; 00050 guint64 sum, carry, roll, pmax; 00051 00052 prod.isneg = 0; 00053 if (0 > a) 00054 { 00055 prod.isneg = !prod.isneg; 00056 a = -a; 00057 } 00058 00059 if (0 > b) 00060 { 00061 prod.isneg = !prod.isneg; 00062 b = -b; 00063 } 00064 00065 a1 = a >> 32; 00066 a0 = a - (a1 << 32); 00067 00068 b1 = b >> 32; 00069 b0 = b - (b1 << 32); 00070 00071 d = a0 * b0; 00072 d1 = d >> 32; 00073 d0 = d - (d1 << 32); 00074 00075 e = a0 * b1; 00076 e1 = e >> 32; 00077 e0 = e - (e1 << 32); 00078 00079 f = a1 * b0; 00080 f1 = f >> 32; 00081 f0 = f - (f1 << 32); 00082 00083 g = a1 * b1; 00084 g1 = g >> 32; 00085 g0 = g - (g1 << 32); 00086 00087 sum = d1 + e0 + f0; 00088 carry = 0; 00089 /* Can't say 1<<32 cause cpp will goof it up; 1ULL<<32 might work */ 00090 roll = 1 << 30; 00091 roll <<= 2; 00092 00093 pmax = roll - 1; 00094 while (pmax < sum) 00095 { 00096 sum -= roll; 00097 carry ++; 00098 } 00099 00100 prod.lo = d0 + (sum << 32); 00101 prod.hi = carry + e1 + f1 + g0 + (g1 << 32); 00102 // prod.isbig = (prod.hi || (sum >> 31)); 00103 prod.isbig = prod.hi || (prod.lo >> 63); 00104 00105 return prod; 00106 } 00107 00109 qofint128 00110 shift128 (qofint128 x) 00111 { 00112 guint64 sbit = x.hi & 0x1; 00113 x.hi >>= 1; 00114 x.lo >>= 1; 00115 x.isbig = 0; 00116 if (sbit) 00117 { 00118 x.lo |= HIBIT; 00119 x.isbig = 1; 00120 return x; 00121 } 00122 if (x.hi) 00123 { 00124 x.isbig = 1; 00125 } 00126 return x; 00127 } 00128 00130 qofint128 00131 shiftleft128 (qofint128 x) 00132 { 00133 guint64 sbit; 00134 sbit = x.lo & HIBIT; 00135 x.hi <<= 1; 00136 x.lo <<= 1; 00137 x.isbig = 0; 00138 if (sbit) 00139 { 00140 x.hi |= 1; 00141 x.isbig = 1; 00142 return x; 00143 } 00144 if (x.hi) 00145 { 00146 x.isbig = 1; 00147 } 00148 return x; 00149 } 00150 00152 qofint128 00153 inc128 (qofint128 a) 00154 { 00155 if (0 == a.isneg) 00156 { 00157 a.lo ++; 00158 if (0 == a.lo) 00159 { 00160 a.hi ++; 00161 } 00162 } 00163 else 00164 { 00165 if (0 == a.lo) 00166 { 00167 a.hi --; 00168 } 00169 a.lo --; 00170 } 00171 00172 a.isbig = (a.hi != 0) || (a.lo >> 63); 00173 return a; 00174 } 00175 00179 qofint128 00180 div128 (qofint128 n, gint64 d) 00181 { 00182 qofint128 quotient; 00183 int i; 00184 guint64 remainder = 0; 00185 00186 quotient = n; 00187 if (0 > d) 00188 { 00189 d = -d; 00190 quotient.isneg = !quotient.isneg; 00191 } 00192 00193 /* Use grade-school long division algorithm */ 00194 for (i = 0; i < 128; i++) 00195 { 00196 guint64 sbit = HIBIT & quotient.hi; 00197 remainder <<= 1; 00198 if (sbit) remainder |= 1; 00199 quotient = shiftleft128 (quotient); 00200 if (remainder >= d) 00201 { 00202 remainder -= d; 00203 quotient.lo |= 1; 00204 } 00205 } 00206 00207 /* compute the carry situation */ 00208 quotient.isbig = (quotient.hi || (quotient.lo >> 63)); 00209 00210 return quotient; 00211 } 00212 00218 gint64 00219 rem128 (qofint128 n, gint64 d) 00220 { 00221 qofint128 quotient = div128 (n, d); 00222 00223 qofint128 mu = mult128 (quotient.lo, d); 00224 00225 gint64 nn = 0x7fffffffffffffffULL & n.lo; 00226 gint64 rr = 0x7fffffffffffffffULL & mu.lo; 00227 return nn - rr; 00228 } 00229 00231 gboolean 00232 equal128 (qofint128 a, qofint128 b) 00233 { 00234 if (a.lo != b.lo) return 0; 00235 if (a.hi != b.hi) return 0; 00236 if (a.isneg != b.isneg) return 0; 00237 return 1; 00238 } 00239 00241 int 00242 cmp128 (qofint128 a, qofint128 b) 00243 { 00244 if ((0 == a.isneg) && b.isneg) return 1; 00245 if (a.isneg && (0 == b.isneg)) return -1; 00246 if (0 == a.isneg) 00247 { 00248 if (a.hi > b.hi) return 1; 00249 if (a.hi < b.hi) return -1; 00250 if (a.lo > b.lo) return 1; 00251 if (a.lo < b.lo) return -1; 00252 return 0; 00253 } 00254 00255 if (a.hi > b.hi) return -1; 00256 if (a.hi < b.hi) return 1; 00257 if (a.lo > b.lo) return -1; 00258 if (a.lo < b.lo) return 1; 00259 return 0; 00260 } 00261 00263 guint64 00264 gcf64(guint64 num, guint64 denom) 00265 { 00266 guint64 t; 00267 00268 t = num % denom; 00269 num = denom; 00270 denom = t; 00271 00272 /* The strategy is to use Euclid's algorithm */ 00273 while (0 != denom) 00274 { 00275 t = num % denom; 00276 num = denom; 00277 denom = t; 00278 } 00279 /* num now holds the GCD (Greatest Common Divisor) */ 00280 return num; 00281 } 00282 00284 qofint128 00285 lcm128 (guint64 a, guint64 b) 00286 { 00287 guint64 gcf = gcf64 (a, b); 00288 b /= gcf; 00289 return mult128 (a, b); 00290 } 00291 00293 qofint128 00294 add128 (qofint128 a, qofint128 b) 00295 { 00296 qofint128 sum; 00297 if (a.isneg == b.isneg) 00298 { 00299 sum.isneg = a.isneg; 00300 sum.hi = a.hi + b.hi; 00301 sum.lo = a.lo + b.lo; 00302 if ((sum.lo < a.lo) || (sum.lo < b.lo)) 00303 { 00304 sum.hi ++; 00305 } 00306 sum.isbig = sum.hi || (sum.lo >> 63); 00307 return sum; 00308 } 00309 if ((b.hi > a.hi) || 00310 ((b.hi == a.hi) && (b.lo > a.lo))) 00311 { 00312 qofint128 tmp = a; 00313 a = b; 00314 b = tmp; 00315 } 00316 00317 sum.isneg = a.isneg; 00318 sum.hi = a.hi - b.hi; 00319 sum.lo = a.lo - b.lo; 00320 00321 if (sum.lo > a.lo) 00322 { 00323 sum.hi --; 00324 } 00325 00326 sum.isbig = sum.hi || (sum.lo >> 63); 00327 return sum; 00328 } 00329 00330 00331 #ifdef TEST_128_BIT_MULT 00332 00333 static void pr (gint64 a, gint64 b) 00334 { 00335 qofint128 prod = mult128 (a, b); 00336 printf ("%" G_GINT64_FORMAT " * %" G_GINT64_FORMAT " = %" 00337 G_GUINT64_FORMAT " %" G_GUINT64_FORMAT " (0x%" 00338 G_GINT64_MODIFIER "x %" G_GINT64_MODIFIER "x) %hd\n", 00339 a, b, prod.hi, prod.lo, prod.hi, prod.lo, prod.isbig); 00340 } 00341 00342 static void prd (gint64 a, gint64 b, gint64 c) 00343 { 00344 qofint128 prod = mult128 (a, b); 00345 qofint128 quot = div128 (prod, c); 00346 gint64 rem = rem128 (prod, c); 00347 printf ("%" G_GINT64_FORMAT " * %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT 00348 " = %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT " + %" 00349 G_GINT64_FORMAT " (0x%" G_GINT64_MODIFIER "x %" 00350 G_GINT64_MODIFIER "x) %hd\n", 00351 a, b, c, quot.hi, quot.lo, rem, quot.hi, quot.lo, quot.isbig); 00352 } 00353 00354 int main () 00355 { 00356 gint64 x; 00357 qofint128 n; 00358 gint64 d; 00359 qofint128 quot; 00360 int i; 00361 00362 pr (2, 2); 00363 00364 x = 1 << 30; 00365 x <<= 2; 00366 00367 pr (x, x); 00368 pr (x + 1, x); 00369 pr (x + 1, x + 1); 00370 00371 pr (x, -x); 00372 pr (-x, -x); 00373 pr (x - 1, x); 00374 pr (x - 1, x - 1); 00375 pr (x - 2, x - 2); 00376 00377 x <<= 1; 00378 pr (x, x); 00379 pr (x, -x); 00380 00381 pr (1000000, G_GINT64_CONSTANT(10000000000000)); 00382 00383 prd (x, x, 2); 00384 prd (x, x, 3); 00385 prd (x, x, 4); 00386 prd (x, x, 5); 00387 prd (x, x, 6); 00388 00389 x <<= 29; 00390 prd (3, x, 3); 00391 prd (6, x, 3); 00392 prd (99, x, 3); 00393 prd (100, x, 5); 00394 prd (540, x, 5); 00395 prd (777, x, 7); 00396 prd (1111, x, 11); 00397 00398 /* Really test division */ 00399 n.hi = 0xdd91; 00400 n.lo = 0x6c5abefbb9e13480ULL; 00401 00402 d = 0x2ae79964d3ae1d04ULL; 00403 00404 for (i = 0; i < 20; i++) 00405 { 00406 00407 quot = div128 (n, d); 00408 printf ("%d result = %" G_GINT64_MODIFIER "x %" G_GINT64_MODIFIER "x\n", 00409 i, quot.hi, quot.lo); 00410 d >>= 1; 00411 n = shift128 (n); 00412 } 00413 return 0; 00414 } 00415 00416 #endif /* TEST_128_BIT_MULT */ 00417 00418 /* ======================== END OF FILE =================== */
1.7.4