/* * 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)); }