home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_11_02 / 1102042a < prev    next >
Text File  |  1992-12-07  |  4KB  |  135 lines

  1. /******************************************************
  2.  *
  3.  *            fp2ratio.c
  4.  *
  5.  *    Floating-point number to ratio approximation.
  6.  *
  7.  *    Usage: fp2ratio number
  8.  *
  9.  *    Compiler: Microsoft C 6.0 using inline
  10.  *    floating-point emulator (option /FPi).
  11.  *
  12.  *****************************************************/
  13.  
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <math.h>
  17.  
  18. /* Maximum argument significant digits. */
  19. #define MAX_SIG_DIGITS        9
  20.  
  21. #define ARRAY_SIZE          2
  22. #define I        ( i     ) % ARRAY_SIZE
  23. #define I_MINUS_1    ( i - 1 ) % ARRAY_SIZE
  24. #define I_MINUS_2       ( i     ) % ARRAY_SIZE
  25.  
  26. typedef    unsigned long     Ulong;
  27. typedef    long double       Ldouble;
  28.  
  29.  
  30. int main( int argc, char **argv )
  31. {
  32.    int     i, dec_digits, sign;
  33.    Ulong   n0, n, d0, d, temp;
  34.    Ulong   p[ARRAY_SIZE] = { 0, 1 };
  35.    Ulong   q[ARRAY_SIZE] = { 1, 0 };
  36.    double  x;
  37.    Ldouble percent_err;
  38.  
  39.    /* Check for one command-line argument. */
  40.    if ( argc != 2 )
  41.    {
  42.       fprintf(stderr, "Usage: %s number\n", argv[0]);
  43.       exit( EXIT_FAILURE );
  44.    }
  45.    else /* argc == 2 */
  46.       x = strtod( argv[1], (char **) NULL );
  47.  
  48.    /* Handle zero and negative arguments. */
  49.    if ( x < 0.0 )
  50.    {
  51.       sign = -1;
  52.       x    = -x;
  53.    }
  54.    else if ( x == 0.0 )
  55.    {
  56.       puts( "\n0:\n" );
  57.       puts( "          0 / 1             Exactly!" );
  58.       exit( EXIT_SUCCESS );
  59.    }
  60.    else /* x > 0.0 */
  61.       sign = 1;
  62.  
  63.    /* Check for out-of-range arguments. */
  64.    if ( x >= pow( 10.0, (double) MAX_SIG_DIGITS ) )
  65.    {
  66.       fprintf( stderr, "%s: Magnitude is", argv[1] );
  67.       fprintf( stderr, " too large.\n" );
  68.       exit( EXIT_FAILURE );
  69.    }
  70.    else if ( x <=
  71.       pow( 10.0, (double) -MAX_SIG_DIGITS) / 2.0 )
  72.    {
  73.       fprintf( stderr, "%s: Magnitude is", argv[1] );
  74.       fprintf( stderr, " too small.\n" );
  75.       exit( EXIT_FAILURE );
  76.    }
  77.  
  78.    /* Determine the argument's radix-10 ratio. */
  79.    d0 = (Ulong) pow( 10.0, (double) MAX_SIG_DIGITS -
  80.         ((x < 1.0) ? 0.0 : floor( 1.0 + log10( x ))));
  81.    n0 = (Ulong) ( ( x * (double) d0 ) + 0.5 );
  82.    printf( "\n%.*g:\n\n", MAX_SIG_DIGITS,
  83.            (double) n0 / (double) d0 );
  84.  
  85.    /* Iteratively determine integer ratios. */
  86.    for ( i = 2, d = d0, n = n0 ; ;
  87.          i++, temp = d, d = n % d, n = temp )
  88.    {
  89.       p[I] = n / d * p[I_MINUS_1] + p[I_MINUS_2];
  90.       q[I] = n / d * q[I_MINUS_1] + q[I_MINUS_2];
  91.  
  92.       /* Print ratios with non-zero numerators. */
  93.       if ( p[I] != 0 )
  94.       {
  95.          printf("%11ld / %-10lu", sign * p[I], q[I]);
  96.          if ( n % d == 0 )
  97.          {
  98.             printf( "    Exactly!\n" );
  99.             break;
  100.          }
  101.  
  102.          /*
  103.       *  Compute the ratio's percent error
  104.       *  (taking care to avoid significance
  105.       *  loss when subtracting nearly equal
  106.       *  values):
  107.       *
  108.       *    percent error =
  109.       *
  110.       *
  111.       *     100 * ( p[i] * d0  -  q[i] * n0 )
  112.       *    -------------------------------------
  113.       *             q[I] * n0
  114.       *
  115.       *  Display in %f format with at least two
  116.       *  significant digits.
  117.       */
  118.          percent_err  = (Ldouble) p[I] * (Ldouble) d0;
  119.          percent_err -= (Ldouble) q[I] * (Ldouble) n0;
  120.      percent_err *= 100.0L;
  121.      percent_err /= (Ldouble) q[I] * (Ldouble) n0;
  122.  
  123.          dec_digits =
  124.             ( fabs( (double) percent_err ) >= 10.0 ) ?
  125.             1 : 1 + ( int ) ( fabs( floor(
  126.             log10( fabs( (double) percent_err )))));
  127.          printf( "%+*.*Lf%%\n", dec_digits + 6,
  128.                  dec_digits, percent_err );
  129.       }
  130.    }
  131.  
  132.    exit( EXIT_SUCCESS );
  133. }
  134.  
  135.