Listing 1 fp2ratio.c — an implementation of Algorithm B in Figure 2 in ANSI C

/******************************************************
 *
 *                       fp2ratio.c
 *
 *       Floating-point number to ratio approximation.
 *
 *       Usage: fp2ratio number
 *
 *       Compiler: Microsoft C 6.0 using inline
 *       floating-point emulator (option /FPi).
 *
 ******************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/* Maximum argument significant digits. */
#define MAX_SIG_DIGITS      9

#define ARRAY_SIZE          2
#define I               ( i     ) % ARRAY_SIZE
#define I MINUS_1       ( i - 1 ) % ARRAY_SIZE
#define I_MINUS_2       ( i     ) % ARRAY_SIZE

typedef unsigned long     Ulong;
typedef long double       Ldouble;

int main( int argc, char **argv )
{
   int     i, dec_digits, sign;
   Ulong   n0, n, d0, d, temp;
   Ulong   p[ARRAY_SIZE] = { 0, 1 };
   Ulong   q[ARRAY_SIZE] = { 1, 0 };
   double  x;
   Ldouble percent_err;
   
   /* Check for one command-line argument. */
   if ( argc != 2 )
   {
      fprintf(stderr, "Usage: %s number\n", argv[0);
      exit( EXIT_FAILURE );
   }
   else /* argc == 2 */
      x = strtod( argv[1], (char **) NULL );
   
   /* Handle zero and negative arguments. */
   if ( x < 0.0 )
   {
      sign = -1;
      x     = -x;
   }
   else if ( x == 0.0 )
   {
      puts( "\n0:\n" );
      puts( "         0 / 1             Exactly!" );
      exit( EXIT_SUCCESS );
   }
   else /* x > 0.0 */
      sign = 1;
   
   /* Check for out-of-range arguments. */
   if ( x >= pow( 10.0, (double) MAX_SIG_DIGITS ) )
   {
      fprintf( stderr, "%s: Magnitude is", argv[1] );
      fprintf( stderr, " too large.\n" );
      exit( EXIT_FAILURE );
   }
   else if ( x <=
      pow( 10.0, (double) -MAX_SIG_DIGITS) / 2.0 )
   {
      fprintf( stderr, "%s: Magnitude is", argv[1] );
      fprintf( stderr, "too small.\n" );
      exit( EXIT_FAILURE );
   }
   
   /* Determine the argument's radix-10 ratio. */
   d0 = (Ulong) pow( 10.0, (double) MAX_SIG_DIGITS -
       ((x < 1.0) ? 0.0 : floor( 1.0 + log10( x ))));
   n0 = (Ulong) ( ( x * (double) d0 ) + 0.5 );
   printf( "\n%.*g:\n\n", MAX_SIG_DIGITS,
          (double) n0 / (double) d0 );
   
   /* Iteratively determine integer ratios. */
   for ( i = 2, d = d0, n = n0 ; ;
        i++, temp = d, d = n % d, n = temp )
   {
      p[I] = n / d * p[I_MINUS_1] + p[I_MINUS_2];
      q[I] = n / d * q[I_MINUS_1] + q[I_MINUS_2];
      
      /* Print ratios with non-zero numerators. */
      if ( p[I] != 0 )
      {
         printf("%11ld / %-10lu", sign * p[I], q[I]);
         if ( n % d == 0 )
         {
            printf("  Exactly!\n" );
            break;
         }
         
         /*
          *  Compute the ratio's percent error
          *  (taking care to avoid significance
          *  loss when subtracting nearly equal
          *  values):
          *
          *    percent error =
          *
          *
          *      100 * ( p[i] * d0 - q[i] * n0 )
          *    -----------------------------------
          *                  q[I] * n0
          *
          *  Display in %f format with at least two
          *  significant digits.
          */
         percent_err = (Ldouble)p[I] * (Ldouble)d0;
         percent_err -= (Ldouble)q[I] * (Ldouble)n0;
         percent_err *= 100.0L;
         percent_err /= (Ldouble)q[I] * (Ldouble)n0;
         
         dec_digits =
           ( fabs( (double) percent_err ) >= 10.0 ) ?
           1 : 1 + ( int ) ( fabs( floor(
           log10( fabs((double) percent_err )))));
         printf( "%+*.*Lf%%\n", dec_digits + 6,
                dec_digits, percent_err );
      }
   }
   
   exit( EXIT_SUCCESS );
}
/* End of File */