[Gnucash-changes] -- finish work on add128 -- make subtract a
special case of add
Linas Vepstas
linas at cvs.gnucash.org
Sat Jun 26 19:01:39 EDT 2004
Log Message:
-----------
-- finish work on add128
-- make subtract a special case of add (instead of copying code)
-- improve overflow characteristics of divide
Modified Files:
--------------
gnucash/src/engine:
gnc-numeric.c
Revision Data
-------------
Index: gnc-numeric.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/gnc-numeric.c,v
retrieving revision 1.41
retrieving revision 1.42
diff -Lsrc/engine/gnc-numeric.c -Lsrc/engine/gnc-numeric.c -u -r1.41 -r1.42
--- src/engine/gnc-numeric.c
+++ src/engine/gnc-numeric.c
@@ -259,7 +259,7 @@
return mult128 (a,b);
}
-/** Add a pair of 128-bit numbers, returning a 128bit number */
+/** Add a pair of 128-bit numbers, returning a 128-bit number */
static inline gncint128
add128 (gncint128 a, gncint128 b)
{
@@ -276,7 +276,25 @@
sum.isbig = sum.hi || (sum.lo >> 63);
return sum;
}
-/* XXXXXXXXXXXXXXXXXXXXXXXXXXX not done */
+ if ((b.hi > a.hi) ||
+ ((b.hi == a.hi) && (b.lo > a.lo)))
+ {
+ gncint128 tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ sum.isneg = a.isneg;
+ sum.hi = a.hi - b.hi;
+ sum.lo = a.lo - b.lo;
+
+ if (sum.lo > a.lo)
+ {
+ sum.hi --;
+ }
+
+ sum.isbig = sum.hi || (sum.lo >> 63);
+ return sum;
}
#ifdef TEST_128_BIT_MULT
@@ -568,12 +586,14 @@
{
gnc_numeric sum;
- if(gnc_numeric_check(a) || gnc_numeric_check(b)) {
+ if(gnc_numeric_check(a) || gnc_numeric_check(b))
+ {
return gnc_numeric_error(GNC_ERROR_ARG);
}
if((denom == GNC_DENOM_AUTO) &&
- (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED) {
+ (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED)
+ {
if(a.denom == b.denom) {
denom = a.denom;
}
@@ -588,30 +608,33 @@
}
}
- if(a.denom < 0) {
+ if(a.denom < 0)
+ {
a.num *= a.denom;
a.denom = 1;
}
- if(b.denom < 0) {
+ if(b.denom < 0)
+ {
b.num *= b.denom;
b.denom = 1;
}
- /* get an exact answer.. same denominator is the common case. */
- if(a.denom == b.denom) {
+ /* Get an exact answer.. same denominator is the common case. */
+ if(a.denom == b.denom)
+ {
sum.num = a.num + b.num;
sum.denom = a.denom;
}
else
{
- gint64 lcd;
/* We want to do this:
* sum.num = a.num*b.denom + b.num*a.denom;
* sum.denom = a.denom*b.denom;
- * but the multiply could overflow.
+ * but the multiply could overflow.
* Computing the LCD minimizes likelyhood of overflow
*/
+ gint64 lcd;
lcd = gnc_numeric_lcd(a,b);
if (GNC_ERROR_ARG == lcd)
{
@@ -623,9 +646,10 @@
gncint128 cb = mult128 (b.num, lcd/b.denom);
if (cb.isbig) return gnc_numeric_error(GNC_ERROR_OVERFLOW);
- /* XXX although we should check for overflow ... */
-// xxxxxxxxxx not done, use add128 for this
- sum.num = ca.lo + cb.lo;
+ gncint128 cab = add128 (ca, cb);
+ if (cab.isbig) return gnc_numeric_error(GNC_ERROR_OVERFLOW);
+
+ sum.num = cab.lo;
sum.denom = lcd;
}
@@ -660,59 +684,14 @@
gnc_numeric_sub(gnc_numeric a, gnc_numeric b,
gint64 denom, gint how)
{
- gnc_numeric diff;
- gint64 lcd;
-
- if(gnc_numeric_check(a) || gnc_numeric_check(b)) {
+ if(gnc_numeric_check(a) || gnc_numeric_check(b))
+ {
return gnc_numeric_error(GNC_ERROR_ARG);
}
- if((denom == GNC_DENOM_AUTO) &&
- (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED) {
- if(a.denom == b.denom) {
- denom = a.denom;
- }
- else if(b.num == 0) {
- denom = a.denom;
- }
- else if(a.num == 0) {
- denom = b.denom;
- }
- else {
- return gnc_numeric_error(GNC_ERROR_DENOM_DIFF);
- }
- }
-
- if(a.denom < 0) {
- a.num *= a.denom;
- a.denom = 1;
- }
-
- if(b.denom < 0) {
- b.num *= b.denom;
- b.denom = 1;
- }
-
- /* get an exact answer.. same denominator is the common case. */
- if(a.denom == b.denom) {
- diff.num = a.num - b.num;
- diff.denom = a.denom;
- }
- else {
- /* ok, convert to the lcd and compute from there... */
- lcd = gnc_numeric_lcd(a,b);
- diff.num = a.num*(lcd/a.denom) - b.num*(lcd/b.denom);
- diff.denom = lcd;
- // diff.num = a.num*b.denom - b.num*a.denom;
- // diff.denom = a.denom*b.denom;
- }
-
- if((denom == GNC_DENOM_AUTO) &&
- ((how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_LCD)) {
- denom = gnc_numeric_lcd(a, b);
- how = how & GNC_NUMERIC_RND_MASK;
- }
- return gnc_numeric_convert(diff, denom, how);
+ gnc_numeric nb = b;
+ nb.num = -nb.num;
+ return gnc_numeric_add (a, nb, denom, how);
}
@@ -812,64 +791,93 @@
{
gnc_numeric quotient;
- if(gnc_numeric_check(a) || gnc_numeric_check(b)) {
+ if(gnc_numeric_check(a) || gnc_numeric_check(b))
+ {
return gnc_numeric_error(GNC_ERROR_ARG);
}
if((denom == GNC_DENOM_AUTO) &&
- (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED) {
- if(a.denom == b.denom) {
+ (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED)
+ {
+ if(a.denom == b.denom)
+ {
denom = a.denom;
}
- else if(a.denom == 0) {
+ else if(a.denom == 0)
+ {
denom = b.denom;
}
- else {
+ else
+ {
return gnc_numeric_error(GNC_ERROR_DENOM_DIFF);
}
}
- if(a.denom < 0) {
+ if(a.denom < 0)
+ {
a.num *= a.denom;
a.denom = 1;
}
- if(b.denom < 0) {
+ if(b.denom < 0)
+ {
b.num *= b.denom;
b.denom = 1;
}
- if(a.denom == b.denom) {
+ if(a.denom == b.denom)
+ {
quotient.num = a.num;
quotient.denom = b.num;
}
else
{
+ gint64 sgn = 1;
+ if (0 > a.num)
+ {
+ sgn = -sgn;
+ a.num = -a.num;
+ }
+ if (0 > b.num)
+ {
+ sgn = -sgn;
+ b.num = -b.num;
+ }
gncint128 nume = mult128(a.num, b.denom);
gncint128 deno = mult128(b.num, a.denom);
if ((0 == nume.isbig) && (0 == deno.isbig))
{
- quotient.num = nume.lo;
- if (nume.isneg) quotient.num = -quotient.num;
+ quotient.num = sgn * nume.lo;
quotient.denom = deno.lo;
}
else if (0 == deno.isbig)
{
- quotient = reduce128 (nume, deno.lo);
+ quotient = reduce128 (nume, deno.lo);
+ quotient.num *= sgn;
}
else
{
- /* Try to avoid overflow by working with the LCD */
- gint64 lcd = gnc_numeric_lcd(a,b);
- nume = mult128 (a.num, (lcd/a.denom));
- deno = mult128 (b.num, (lcd/b.denom));
+ /* Try to avoid overflow by removing common factors */
+ gnc_numeric ra = gnc_numeric_reduce (a);
+ gnc_numeric rb = gnc_numeric_reduce (b);
+
+ gint64 gcf_nume = gcf64(ra.num, rb.denom);
+ gncint128 nume = mult128(ra.num, rb.denom/gcf_nume);
+
+ gint64 gcf_deno = gcf64(rb.num, ra.denom);
+ gncint128 deno = mult128(rb.num, ra.denom/gcf_deno);
+
if ((0 == nume.isbig) && (0 == deno.isbig))
{
- quotient.num = nume.lo;
- if (nume.isneg) quotient.num = -quotient.num;
+ quotient.num = sgn * nume.lo;
quotient.denom = deno.lo;
}
+ else if (0 == deno.isbig)
+ {
+ quotient = reduce128 (nume, deno.lo);
+ quotient.num *= sgn;
+ }
else
{
return gnc_numeric_error (GNC_ERROR_OVERFLOW);
@@ -877,13 +885,15 @@
}
}
- if(quotient.denom < 0) {
+ if(quotient.denom < 0)
+ {
quotient.num = -quotient.num;
quotient.denom = -quotient.denom;
}
if((denom == GNC_DENOM_AUTO) &&
- ((how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_LCD)) {
+ ((how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_LCD))
+ {
denom = gnc_numeric_lcd(a, b);
how = how & GNC_NUMERIC_RND_MASK;
}
More information about the gnucash-changes
mailing list