[gcalctool] Simplify MP calculations



commit a157132d666c8db364889d8e43b4a4d9b4b382d6
Author: Robert Ancell <robert ancell gmail com>
Date:   Tue Nov 3 09:55:21 2009 +1100

    Simplify MP calculations

 src/mp.c |  356 +++++++++++++++++++++----------------------------------------
 1 files changed, 123 insertions(+), 233 deletions(-)
---
diff --git a/src/mp.c b/src/mp.c
index 5a2405a..5cc0acf 100644
--- a/src/mp.c
+++ b/src/mp.c
@@ -942,7 +942,7 @@ mplns(const MPNumber *x, MPNumber *z)
     int t, it0;
     MPNumber t1, t2, t3;
 
-    /* ln(1) = 0 */
+    /* ln(1+0) = 0 */
     if (mp_is_zero(x)) {
         mp_set_from_integer(0, z);
         return;
@@ -965,45 +965,41 @@ mplns(const MPNumber *x, MPNumber *z)
     mp_add_integer(&t1, -1, &t1);
     mp_multiply(x, &t1, z);
 
-    /* START NEWTON ITERATION USING SMALL T, LATER INCREASE */
-    t = max(5, 13 - (MP_BASE << 1));
-    if (t <= MP_T)
+    /* Solve using Newtons method */
+    it0 = t = 5;
+    while(1)
     {
-        it0 = (t + 5) / 2;
-
-        while(1)
-        {
-            int ts2, ts3;
-
-            mp_epowy(z, &t3);
-            mp_add_integer(&t3, -1, &t3);
-            mp_multiply(&t2, &t3, &t1);
-            mp_add(&t3, &t1, &t3);
-            mp_add(&t2, &t3, &t3);
-            mp_subtract(z, &t3, z);
-            if (t >= MP_T)
-                break;
+        int ts2, ts3;
+
+        /* t3 = (e^t3 - 1) */
+        /* z = z - (t2 + t3 + (t2 * t3)) */
+        mp_epowy(z, &t3);
+        mp_add_integer(&t3, -1, &t3);
+        mp_multiply(&t2, &t3, &t1);
+        mp_add(&t3, &t1, &t3);
+        mp_add(&t2, &t3, &t3);
+        mp_subtract(z, &t3, z);
+        if (t >= MP_T)
+            break;
 
-            /*  FOLLOWING LOOP COMPUTES NEXT VALUE OF T TO USE.
-             *  BECAUSE NEWTONS METHOD HAS 2ND ORDER CONVERGENCE,
-             *  WE CAN ALMOST DOUBLE T EACH TIME.
-             */
-            ts3 = t;
-            t = MP_T;
-            do {
-                ts2 = t;
-                t = (t + it0) / 2;
-            } while (t > ts3);
-            t = ts2;
-        }
+        /*  FOLLOWING LOOP COMPUTES NEXT VALUE OF T TO USE.
+         *  BECAUSE NEWTONS METHOD HAS 2ND ORDER CONVERGENCE,
+         *  WE CAN ALMOST DOUBLE T EACH TIME.
+         */
+        ts3 = t;
+        t = MP_T;
+        do {
+            ts2 = t;
+            t = (t + it0) / 2;
+        } while (t > ts3);
+        t = ts2;
+    }
 
-        /* CHECK THAT NEWTON ITERATION WAS CONVERGING AS EXPECTED */
-        if (t3.sign != 0 && t3.exponent << 1 > it0 - MP_T) {
-            mperr("*** ERROR OCCURRED IN MPLNS, NEWTON ITERATION NOT CONVERGING PROPERLY ***");
-        }
+    /* CHECK THAT NEWTON ITERATION WAS CONVERGING AS EXPECTED */
+    if (t3.sign != 0 && t3.exponent << 1 > it0 - MP_T) {
+        mperr("*** ERROR OCCURRED IN MPLNS, NEWTON ITERATION NOT CONVERGING PROPERLY ***");
     }
 
-    /* REVERSE SIGN OF Y AND RETURN */
     z->sign = -z->sign;
 }
 
@@ -1405,86 +1401,27 @@ mp_invert_sign(const MPNumber *x, MPNumber *z)
 void
 mp_normalize(MPNumber *x)
 {
-    int i__1, i, j, b2, i2, i2m, round;
+    int start_index;
 
-    /* Normalized zero is zero */
-    if (mp_is_zero(x))
-        return;
+    /* Find first non-zero digit */
+    for (start_index = 0; start_index < MP_SIZE && x->fraction[start_index] == 0; start_index++);
 
-    /* CHECK THAT SIGN = +-1 */
-    if (abs(x->sign) > 1) {
-        mperr("*** SIGN NOT 0, +1 OR -1 IN CALL TO MP_NORMALIZE, POSSIBLE OVERWRITING PROBLEM ***");
-        mp_set_from_integer(0, x);
+    /* Mark as zero */
+    if (start_index >= MP_SIZE) {
+        x->sign = 0;
+        x->exponent = 0;
         return;
     }
 
-    i2 = MP_T + 4;
-
-    /* Normalize by shifting the fraction to the left */
-    if (x->fraction[0] == 0) {
-        /* Find how many places to shift and detect 0 fraction */
-        for (i = 1; i < i2 && x->fraction[i] == 0; i++);
-        if (i == i2) {
-            mp_set_from_integer(0, x);
-            return;
-        }
-
-        x->exponent -= i;
-        i2m = i2 - i;
-        for (j = 0; j < i2m; j++)
-            x->fraction[j] = x->fraction[j + i];
-        for (; j < i2; j++)
-            x->fraction[j] = 0;
-    }
-
-    /*  SEE IF ROUNDING NECESSARY
-     *  TREAT EVEN AND ODD BASES DIFFERENTLY
-     */
-    b2 = MP_BASE / 2;
-    if (b2 << 1 != MP_BASE) {
-        round = 0;
-        /* ODD BASE, ROUND IF R(T+1)... > 1/2 */
-        for (i = 0; i < 4; i++) {
-            i__1 = x->fraction[MP_T + i] - b2;
-            if (i__1 < 0)
-                break;
-            else if (i__1 == 0)
-                continue;
-            else {
-                round = 1;
-                break;
-            }
-        }
-    }
-    else {
-        /*  B EVEN.  ROUND IF R(T+1) >= B2 UNLESS R(T) ODD AND ALL ZEROS
-         *  AFTER R(T+2).
-         */
-        round = 1;
-        i__1 = x->fraction[MP_T] - b2;
-        if (i__1 < 0)
-            round = 0;
-        else if (i__1 == 0) {
-            if (x->fraction[MP_T - 1] % 2 != 0) {
-                if (x->fraction[MP_T + 1] + x->fraction[MP_T + 2] + x->fraction[MP_T + 3] == 0)
-                    round = 0;
-            }
-        }
-    }
+    /* Shift left so first digit is non-zero */
+    if (start_index > 0) {
+        int i;
 
-    /* ROUND */
-    if (round) {
-        for (j = MP_T - 1; j >= 0; j--) {
-            ++x->fraction[j];
-            if (x->fraction[j] < MP_BASE)
-                break;
-            x->fraction[j] = 0;
-        }
-        /* EXCEPTIONAL CASE, ROUNDED UP TO .10000... */
-        if (j < 0) {
-            x->exponent++;
-            x->fraction[0] = 1;
-        }
+        x->exponent -= start_index;
+        for (i = 0; (i + start_index) < MP_SIZE; i++)
+            x->fraction[i] = x->fraction[i + start_index];
+        for (; i < MP_SIZE; i++)
+            x->fraction[i] = 0;
     }
 }
 
@@ -1520,20 +1457,11 @@ mp_pwr(const MPNumber *x, const MPNumber *y, MPNumber *z)
 }
 
 
-/*  RETURNS Z = 1/X, FOR MP X AND Z.
- *  MP_ROOT (X, -1, Z) HAS THE SAME EFFECT.
- *  DIMENSION OF R MUST BE AT LEAST 4*T+10 IN CALLING PROGRAM
- *  (BUT Z(1) MAY BE R(3T+9)).
- *  NEWTONS METHOD IS USED, SO FINAL ONE OR TWO DIGITS MAY
- *  NOT BE CORRECT.
- */
 void
 mp_reciprocal(const MPNumber *x, MPNumber *z)
 {
-    MPNumber tmp_x, t1, t2;
-    int ex, it0, t;
-    float rx;
-    static int it[9] = { 0, 8, 6, 5, 4, 4, 4, 4, 4 };
+    MPNumber t1, t2;
+    int it0, t;
 
     /* 1/0 invalid */
     if (mp_is_zero(x)) {
@@ -1542,61 +1470,44 @@ mp_reciprocal(const MPNumber *x, MPNumber *z)
         return;
     }
 
-    ex = x->exponent;
-
-    /* SET EXPONENT TO ZERO SO RX NOT TOO LARGE OR SMALL. */
-    /* work-around to avoid touching X */
-    mp_set_from_mp(x, &tmp_x);
-    tmp_x.exponent = 0;
-    rx = mp_cast_to_float(&tmp_x);
-
-    /* USE SINGLE-PRECISION RECIPROCAL AS FIRST APPROXIMATION */
-    mp_set_from_float(1.0f / rx, &t1);
-
-    /* CORRECT EXPONENT OF FIRST APPROXIMATION */
-    t1.exponent -= ex;
+    /* Start by approximating value using floating point */
+    mp_set_from_mp(x, &t1);
+    t1.exponent = 0;
+    mp_set_from_float(1.0f / mp_cast_to_float(&t1), &t1);
+    t1.exponent -= x->exponent;
 
-    /* START WITH SMALL T TO SAVE TIME. ENSURE THAT B**(T-1) >= 100 */
-    if (MP_BASE < 10)
-        t = it[MP_BASE - 1];
-    else
-        t = 3;
-    it0 = (t + 4) / 2;
+    it0 = t = 3;
+    while(1) {
+        int ts2, ts3;
 
-    /* MAIN ITERATION LOOP */
-    if (t <= MP_T) {
-        while(1) {
-            int ts2, ts3;
-
-            mp_multiply(x, &t1, &t2);
-            mp_add_integer(&t2, -1, &t2);
-            mp_multiply(&t1, &t2, &t2);
-            mp_subtract(&t1, &t2, &t1);
-            if (t >= MP_T)
-                break;
+        /* t1 = t1 - (t1 * ((x * t1) - 1))   (2*t1 - t1^2*x) */
+        mp_multiply(x, &t1, &t2);
+        mp_add_integer(&t2, -1, &t2);
+        mp_multiply(&t1, &t2, &t2);
+        mp_subtract(&t1, &t2, &t1);
+        if (t >= MP_T)
+            break;
 
-            /*  FOLLOWING LOOP ALMOST DOUBLES T (POSSIBLE
-             *  BECAUSE NEWTONS METHOD HAS 2ND ORDER CONVERGENCE).
-             */
-            ts3 = t;
-            t = MP_T;
-            do {
-                ts2 = t;
-                t = (t + it0) / 2;
-            } while (t > ts3);
-            t = min(ts2, MP_T);
-        }
+        /*  FOLLOWING LOOP ALMOST DOUBLES T (POSSIBLE
+         *  BECAUSE NEWTONS METHOD HAS 2ND ORDER CONVERGENCE).
+         */
+        ts3 = t;
+        t = MP_T;
+        do {
+            ts2 = t;
+            t = (t + it0) / 2;
+        } while (t > ts3);
+        t = min(ts2, MP_T);
+    }
 
-        /* RETURN IF NEWTON ITERATION WAS CONVERGING */
-        if (t2.sign != 0 && (t1.exponent - t2.exponent) << 1 < MP_T - it0) {
-            /*  THE FOLLOWING MESSAGE MAY INDICATE THAT B**(T-1) IS TOO SMALL,
-             *  OR THAT THE STARTING APPROXIMATION IS NOT ACCURATE ENOUGH.
-             */
-            mperr("*** ERROR OCCURRED IN MP_RECIPROCAL, NEWTON ITERATION NOT CONVERGING PROPERLY ***");
-        }
+    /* RETURN IF NEWTON ITERATION WAS CONVERGING */
+    if (t2.sign != 0 && (t1.exponent - t2.exponent) << 1 < MP_T - it0) {
+        /*  THE FOLLOWING MESSAGE MAY INDICATE THAT B**(T-1) IS TOO SMALL,
+         *  OR THAT THE STARTING APPROXIMATION IS NOT ACCURATE ENOUGH.
+         */
+        mperr("*** ERROR OCCURRED IN MP_RECIPROCAL, NEWTON ITERATION NOT CONVERGING PROPERLY ***");
     }
 
-    /* MOVE RESULT TO Y AND RETURN AFTER RESTORING T */
     mp_set_from_mp(&t1, z);
 }
 
@@ -1604,11 +1515,9 @@ mp_reciprocal(const MPNumber *x, MPNumber *z)
 void
 mp_root(const MPNumber *x, int n, MPNumber *z)
 {
-    float r__1;
+    float approximation;
     int ex, np, it0, t;
-    float rx;
     MPNumber t1, t2;
-    static const int it[9] = { 0, 8, 6, 5, 4, 4, 4, 4, 4 };
 
     /* x^(1/1) = x */
     if (n == 1) {
@@ -1640,6 +1549,7 @@ mp_root(const MPNumber *x, int n, MPNumber *z)
         return;
     }
 
+    // FIXME: Imaginary root
     if (x->sign < 0 && np % 2 == 0) {
         mperr(_("nth root of negative number not defined for even n"));
         mp_set_from_integer(0, z);
@@ -1649,72 +1559,52 @@ mp_root(const MPNumber *x, int n, MPNumber *z)
     /* DIVIDE EXPONENT BY NP */
     ex = x->exponent / np;
 
-    /* REDUCE EXPONENT SO RX NOT TOO LARGE OR SMALL. */
-    {
-        MPNumber tmp_x;
-        mp_set_from_mp(x, &tmp_x);
-        tmp_x.exponent = 0;
-        rx = mp_cast_to_float(&tmp_x);
-    }
-
-    /* USE SINGLE-PRECISION ROOT FOR FIRST APPROXIMATION */
-    r__1 = exp(((float)(np * ex - x->exponent) * log((float)MP_BASE) -
-           log((fabs(rx)))) / (float)np);
-    mp_set_from_float(r__1, &t1);
-
-    /* SIGN OF APPROXIMATION SAME AS THAT OF X */
+    /* Get initial approximation */
+    mp_set_from_mp(x, &t1);
+    t1.exponent = 0;
+    approximation = exp(((float)(np * ex - x->exponent) * log((float)MP_BASE) -
+                         log((fabs(mp_cast_to_float(&t1))))) / (float)np);
+    mp_set_from_float(approximation, &t1);
     t1.sign = x->sign;
-
-    /* CORRECT EXPONENT OF FIRST APPROXIMATION */
     t1.exponent -= ex;
 
-    /* START WITH SMALL T TO SAVE TIME */
-    /* ENSURE THAT B**(T-1) >= 100 */
-    if (MP_BASE < 10)
-        t = it[MP_BASE - 1];
-    else
-        t = 3;
-
-    if (t <= MP_T) {
-        /* IT0 IS A NECESSARY SAFETY FACTOR */
-        it0 = (t + 4) / 2;
-
-        /* MAIN ITERATION LOOP */
-        while(1) {
-            int ts2, ts3;
-
-            mp_xpowy_integer(&t1, np, &t2);
-            mp_multiply(x, &t2, &t2);
-            mp_add_integer(&t2, -1, &t2);
-            mp_multiply(&t1, &t2, &t2);
-            mp_divide_integer(&t2, np, &t2);
-            mp_subtract(&t1, &t2, &t1);
-
-            /*  FOLLOWING LOOP ALMOST DOUBLES T (POSSIBLE BECAUSE
-             *  NEWTONS METHOD HAS 2ND ORDER CONVERGENCE).
-             */
-            if (t >= MP_T)
-                break;
+    /* MAIN ITERATION LOOP */
+    it0 = t = 3;
+    while(1) {
+        int ts2, ts3;
 
-            ts3 = t;
-            t = MP_T;
-            do {
-                ts2 = t;
-                t = (t + it0) / 2;
-            } while (t > ts3);
-            t = min(ts2, MP_T);
-        }
+        /* t1 = t1 - ((t1 * ((x * t1^np) - 1)) / np) */
+        mp_xpowy_integer(&t1, np, &t2);
+        mp_multiply(x, &t2, &t2);
+        mp_add_integer(&t2, -1, &t2);
+        mp_multiply(&t1, &t2, &t2);
+        mp_divide_integer(&t2, np, &t2);
+        mp_subtract(&t1, &t2, &t1);
 
-        /*  NOW R(I2) IS X**(-1/NP)
-         *  CHECK THAT NEWTON ITERATION WAS CONVERGING
+        /*  FOLLOWING LOOP ALMOST DOUBLES T (POSSIBLE BECAUSE
+         *  NEWTONS METHOD HAS 2ND ORDER CONVERGENCE).
          */
-        if (t2.sign != 0 && (t1.exponent - t2.exponent) << 1 < MP_T - it0) {
-            /*  THE FOLLOWING MESSAGE MAY INDICATE THAT B**(T-1) IS TOO SMALL,
-             *  OR THAT THE INITIAL APPROXIMATION OBTAINED USING ALOG AND EXP
-             *  IS NOT ACCURATE ENOUGH.
-             */
-            mperr("*** ERROR OCCURRED IN MP_ROOT, NEWTON ITERATION NOT CONVERGING PROPERLY ***");
-        }
+        if (t >= MP_T)
+            break;
+
+        ts3 = t;
+        t = MP_T;
+        do {
+            ts2 = t;
+            t = (t + it0) / 2;
+        } while (t > ts3);
+        t = min(ts2, MP_T);
+    }
+
+    /*  NOW R(I2) IS X**(-1/NP)
+     *  CHECK THAT NEWTON ITERATION WAS CONVERGING
+     */
+    if (t2.sign != 0 && (t1.exponent - t2.exponent) << 1 < MP_T - it0) {
+        /*  THE FOLLOWING MESSAGE MAY INDICATE THAT B**(T-1) IS TOO SMALL,
+         *  OR THAT THE INITIAL APPROXIMATION OBTAINED USING ALOG AND EXP
+         *  IS NOT ACCURATE ENOUGH.
+         */
+        mperr("*** ERROR OCCURRED IN MP_ROOT, NEWTON ITERATION NOT CONVERGING PROPERLY ***");
     }
 
     if (n >= 0) {
@@ -1739,7 +1629,7 @@ mp_sqrt(const MPNumber *x, MPNumber *z)
         MPNumber t;
 
         mp_root(x, -2, &t);
-        i = t.fraction[0];
+        i = t.fraction[0]; // ?
         mp_multiply(x, &t, z);
         mpext(i, z->fraction[0], z);
     }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]