home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
RISC DISC 3
/
RISC_DISC_3.iso
/
resources
/
etexts
/
gems
/
gemsv
/
ch1_4
/
rat.c
next >
Wrap
C/C++ Source or Header
|
1995-04-04
|
6KB
|
135 lines
/****** rat.c ******/
/* Ken Shoemake, 1994 */
#include <math.h>
#include "rat.h"
static void Mul32(UINT32 x, UINT32 y, UINT32 *hi, UINT32 *lo)
{
UINT32 xlo = x&0xFFFF, xhi = (x>>16)&0xFFFF;
UINT32 ylo = y&0xFFFF, yhi = (y>>16)&0xFFFF;
UINT32 t1, t2, t3;
UINT32 lolo, lohi, t1lo, t1hi, t2lo, t2hi, carry;
*lo = xlo * ylo; *hi = xhi * yhi;
t1 = xhi * ylo; t2 = xlo * yhi;
lolo = *lo&0xFFFF; lohi = (*lo>>16)&0xFFFF;
t1lo = t1&0xFFFF; t1hi = (t1>>16)&0xFFFF;
t2lo = t2&0xFFFF; t2hi = (t2>>16)&0xFFFF;
t3 = lohi + t1lo + t2lo;
carry = t3&0xFFFF; lohi = (t3<<16)&0xFFFF;
*hi += t1hi + t2hi + carry; *lo = (lohi<<16) + lolo;
}
/* ratapprox(x,n) returns the best rational approximation to x whose numerator
and denominator are less than or equal to n in absolute value. The denominator
will be positive, and the numerator and denominator will be in lowest terms.
IEEE 32-bit floating point and 32-bit integers are required.
All best rational approximations of a real x may be obtained from x's
continued fraction representation, x = c0 + 1/(c1 + 1/(c2 + 1/(...)))
by truncation to k terms and possibly "interpolation" of the last term.
The continued fraction expansion itself is obtained by a variant of the
standard GCD algorithm, which is folded into the recursions generating
successive numerators and denominators. These recursions both have the
same form: f[k] = c[k]*f[k-1] + f[k-2]. For further information, see
Fundamentals of Mathematics, Volume I, MIT Press, 1983.
*/
Rational ratapprox(float x, INT32 limit)
{
float tooLargeToFix = ldexp(1.0, BITS); /* 0x4f000000=2147483648.0 */
float tooSmallToFix = ldexp(1.0, -BITS); /* 0x30000000=4.6566e-10 */
float halfTooSmallToFix = ldexp(1.0, -BITS-1); /* 0x2f800000=2.3283e-10 */
int expForInt = 24; /* This exponent in float makes mantissa an INT32 */
static Rational ratZero = {0, 1};
INT32 sign = 1;
BOOL flip = FALSE; /* If TRUE, nk and dk are swapped */
int scale; /* Power of 2 to get x into integer domain */
UINT32 ak2, ak1, ak; /* GCD arguments, initially 1 and x */
UINT32 ck, climit; /* ck is GCD quotient and c.f. term k */
INT32 nk, dk; /* Result num. and den., recursively found */
INT32 nk1 = 0, dk2 = 0; /* History terms for recursion */
INT32 nk2 = 1, dk1 = 1;
BOOL hard = FALSE;
Rational val;
if (limit <= 0) return (ratZero); /* Insist limit > 0 */
if (x < 0.0) {x = -x; sign = -1;}
val.numer = sign; val.denom = limit;
/* Handle first non-zero term of continued fraction,
rest prepared for integer GCD, sure to fit.
*/
if (x >= 1.0) {/* First continued fraction term is non-zero */
float rest;
if (x >= tooLargeToFix || (ck = x) >= limit)
{val.numer = sign*limit; val.denom = 1; return (val);}
flip = TRUE; /* Keep denominator larger, for fast loop test */
nk = 1; dk = ck; /* Make new numerator and denominator */
rest = x - ck;
frexp(1.0,&scale);
scale = expForInt - scale;
ak = ldexp(rest, scale);
ak1 = ldexp(1.0, scale);
} else {/* First continued fraction term is zero */
int n;
UINT32 num = 1;
if (x <= tooSmallToFix) { /* Is x too tiny to be 1/INT32 ? */
if (x <= halfTooSmallToFix) return (ratZero);
if (limit > (UINT32)(0.5/x)) return (val);
else return (ratZero);
}
/* Treating 1.0 and x as integers, divide 1/x in a peculiar way
to get accurate remainder
*/
frexp(x,&scale);
scale = expForInt - scale;
ak1 = ldexp(x, scale);
n = (scale<BITS)?scale:BITS; /* Stay within UINT32 arithmetic */
num <<= n;
ck = num/ak1; /* First attempt at 1/x */
ak = num%ak1; /* First attempt at remainder */
while ((scale -= n) > 0) {/* Shift quotient, remainder until done */
n = (scale<8)?scale:8; /* The 8 is 24 bits of x in 32 bits */
num = ak<<n;
ck = ck<<n + num/ak1;
ak = num%ak1; /* Reduce remainder */
}
/* All done with divide */
if (ck >= limit) { /* Is x too tiny to be 1/limit ? */
if (2*limit > ck)
return (val);
else return (ratZero);
}
nk = 1; dk = ck; /* Make new numer and denom */
}
while (ak != 0) { /* If possible, quit when have exact result */
ak2 = ak1; ak1 = ak; /* Prepare for next term */
nk2 = nk1; nk1 = nk; /* (This loop does almost all the work) */
dk2 = dk1; dk1 = dk;
ck = ak2/ak1; /* Get next term of continued fraction */
ak = ak2 - ck*ak1; /* Get remainder (GCD step) */
climit = (limit - dk2)/dk1; /* Anticipate result of recursion on denom */
if (climit <= ck) {hard = TRUE; break;} /* Do not let denom exceed limit */
nk = ck*nk1 + nk2; /* Make new result numer and denom */
dk = ck*dk1 + dk2;
}
if (hard) {
UINT32 twoClimit = 2*climit;
if (twoClimit >= ck) { /* If climit < ck/2 no improvement possible */
nk = climit*nk1 + nk2; /* Make limited numerator and denominator */
dk = climit*dk1 + dk2;
if (twoClimit == ck) { /* If climit == ck improvement not sure */
/* Using climit is better only when dk2/dk1 > ak/ak1 */
/* For full precision, test dk2*ak1 > dk1*ak */
UINT32 dk2ak1Hi, dk2ak1Lo, dk1akHi, dk1akLo;
Mul32(flip?nk2:dk2, ak1, &dk2ak1Hi, &dk2ak1Lo);
Mul32(flip?nk1:dk1, ak, &dk1akHi, &dk1akLo);
if ((dk2ak1Hi < dk1akHi)
|| ((dk2ak1Hi == dk1akHi) && (dk2ak1Lo <= dk1akLo)))
{ nk = nk1; dk = dk1; } /* Not an improvement, so undo step */
}
}
}
if (flip) {val.numer = sign*dk; val.denom = nk;}
else {val.numer = sign*nk; val.denom = dk;}
return (val);
}