www.pudn.com > geosteiner-3.1.zip > cra.c
/***********************************************************************
File: cra.c
Rev: a-2
Date: 07/28/2000
Copyright (c) 1996, 2001 by David M. Warme
************************************************************************
Closest Rational Approximation.
************************************************************************
Modification Log:
a-1: 08/09/96 warme
: Created.
a-2: 07/28/2000 warme
: Added banner and improved packaging.
************************************************************************/
#include "cra.h"
#include "steiner.h"
/*
* Global Routines
*/
double cra (double z,
double * numout,
double * denout);
/*
* Local Constants
*/
#ifdef __STANDALONE
#define CRA_PRINT 5
#else
#define CRA_PRINT 0
#endif __STANDALONE
#define CRA_FUZZ 1.0e-9
/*
* Local Types
*/
struct cf {
double xi;
struct cf * next;
};
/*
* Local Routines
*/
static void cra_recurse (double orig_z,
double frac,
struct cf * cfp,
double * numout,
double * denout);
/*
* Closest Rational Approximation to the given floating point number.
* Perform continued fraction expansion on the number. Each time a
* new term is added to the expansion, see if the result is close
* enough to the original. Once the CF expansion is close enough,
* we are done.
*/
double
cra (
double z,
double * numout,
double * denout
)
{
double sign, num, den, frac;
struct cf cf;
sign = 1.0;
if (z < 0.0) {
sign = -1.0;
z = -z;
}
if (z <= CRA_FUZZ) return (0.0);
frac = fmod (z, 1.0);
#if CRA_PRINT >= 1
printf ("Initial fraction: %24.15g\n", frac);
#endif
/* First term of CF expansion is the integer part. */
cf.xi = z - frac;
cf.next = NULL;
/* Perform rest of CF expansion. */
cra_recurse (z, frac, &cf, &num, &den);
#if CRA_PRINT >= 1
printf ("%24.15g ~ %24.15g = %f / %f\n",
z, sign * num / den, sign * num, den);
#endif
if (numout NE NULL) {
*numout = sign * num;
}
if (denout NE NULL) {
*denout = den;
}
return (sign * num / den);
}
/*
* Recursive portion of the closest rational approximation code.
* We represent the continued fraction expansion using a linked list.
* Rather than allocating these from the heap, we allocate them on
* the stack and simply recurse deeper to allocate a new one.
*
* It is indeed unfortunate that to reevaluate the newly extended
* CF expansion, it is necessary to start all over again. Were
* there a simple way to incrementally update the numerator and
* denominator values, then this would not be necessary.
*/
static
void
cra_recurse (
double orig_z, /* IN - original Z */
double frac, /* IN - 0 <= frac < 1 */
struct cf * cfp, /* IN - continued fraction expansion */
double * numout, /* OUT - numerator */
double * denout /* OUT - denominator */
)
{
double num, den, a, err, inv, tmp;
struct cf * p;
struct cf cf;
/* Unravel the current continued fraction into a simple */
/* numerator and denominator */
num = 1.0;
den = 0.0;
for (p = cfp; p != NULL; p = p -> next) {
tmp = den + (num * p -> xi);
den = num;
num = tmp;
}
*numout = num;
*denout = den;
a = num / den;
#if CRA_PRINT >= 2
printf ("%24.15g ~ %24.15g = %f / %f\n", orig_z, a, num, den);
#endif
err = (orig_z - a) / orig_z;
if (fabs (err) <= CRA_FUZZ) return;
inv = 1.0 / frac;
frac = fmod (inv, 1.0);
cf.xi = inv - frac;
cf.next = cfp;
cra_recurse (orig_z, frac, &cf, numout, denout);
}
/*
* Main program for use when testing this routine in a stand-alone fashion.
*/
#ifdef __STANDALONE
main ()
{
double a, b;
for (;;) {
scanf (" %lg", &a);
b = cra (a);
printf ("%24.15g ===> %24.15g\n", a, b);
}
}
#endif