⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rat.c

📁 [Game.Programming].Academic - Graphics Gems (6 books source code)
💻 C
字号:
/****** 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>>16)&0xFFFF; lohi = t3&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);                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);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -