diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /systems/knuth/local/mf/mf_arith.c |
Initial commit
Diffstat (limited to 'systems/knuth/local/mf/mf_arith.c')
-rw-r--r-- | systems/knuth/local/mf/mf_arith.c | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/systems/knuth/local/mf/mf_arith.c b/systems/knuth/local/mf/mf_arith.c new file mode 100644 index 0000000000..5347107e81 --- /dev/null +++ b/systems/knuth/local/mf/mf_arith.c @@ -0,0 +1,142 @@ +/* + * mf_arith.c -- optimized arithmetic routines for UNIX METAFONT 84 + * + * These functions account for a significant percentage of the processing + * time used by METAFONT, and have been recoded in assembly language for + * some classes of machines. + * + * As yet I don't have code for SPARC, so I'm just using a C version + * by richards, in hopes that cc does better than pc on the brute-force + * algorithm found in mf.web. + * + * function makefraction(p, q: integer): fraction; + * { Calculate the function floor( (p * 2^28) / q + 0.5 ) } + * { if both p and q are positive. If not, then the value } + * { of makefraction is calculated as though both *were* } + * { positive, then the result sign adjusted. } + * { (e.g. makefraction ALWAYS rounds away from zero) } + * { In case of an overflow, return the largest possible } + * { value (2^32-1) with the correct sign, and set global } + * { variable "aritherror" to 1. Note that -2^32 is } + * { considered to be an illegal product for this type of } + * { arithmetic! } + * + * function takefraction(f: fraction; q: integer): integer; + * { Calculate the function floor( (p * q) / 2^28 + 0.5 ) } + * { takefraction() rounds in a similar manner as } + * { makefraction() above. } + * + */ + +#include "h00vars.h" + +extern bool aritherror; /* to be set on overflow */ + +#define EL_GORDO 0x7fffffff /* 2^31-1 */ +#define FRACTION_ONE 0x10000000 /* 2^28 */ +#define FRACTION_HALF 0x08000000 /* 2^27 */ +#define FRACTION_FOUR 0x40000000 /* 2^30 */ + +int makefraction(p, q) + register int p, q; +{ + int negative; + register int be_careful; + register int f; + int n; + + if (p < 0) { + negative = 1; + p = -p; + } else negative = 0; + + if (q < 0) { + negative = !negative; + q = -q; + } + + n = p / q; + p = p % q; + + if (n >= 8) { + aritherror = TRUE; + return (negative? -EL_GORDO : EL_GORDO); + } + + n = (n-1)*FRACTION_ONE; + f = 1; + do { + be_careful = p - q; + p = be_careful + p; + if (p >= 0) { + f = f + f + 1; + } else { + f <<= 1; + p += q; + } + } while (f < FRACTION_ONE); + + be_careful = p - q; + if ((be_careful + p) >= 0) + f += 1; + return (negative? -(f+n) : (f+n)); + +} + +int takefraction(q, f) + register int q; + register int f; +{ + int n, negative; + register int p, be_careful; + + if (f < 0) { + negative = 1; + f = -f; + } else negative = 0; + if (q < 0) { + negative = !negative; + q = -q; + } + if (f < FRACTION_ONE) + n = 0; + else { + n = f / FRACTION_ONE; + f = f % FRACTION_ONE; + if (q < (EL_GORDO / n)) + n = n * q; + else { + aritherror = TRUE; + n = EL_GORDO; + } + } + + f += FRACTION_ONE; + p = FRACTION_HALF; + if (q < FRACTION_FOUR) + do { + if (f & 0x1) + p = (p+q) >> 1; + else + p >>= 1; + f >>= 1; + } while (f > 1); + else + do { + if (f & 0x1) + p = p + ((q-p) >> 1); + else + p >>= 1; + f >>= 1; + } while (f > 1); + + be_careful = n - EL_GORDO; + if ((be_careful + p) > 0) { + aritherror = TRUE; + n = EL_GORDO - p; + } + + return(negative? -(n+p) : (n+p)); +} + + |