Listing 2

/* Program to create "float.h" by running tests on target system
** using techniques from Kahan a Karpinski Paranoia code
** some of which are credited to W F Cody
** written by T C Prince Sept 1989
** tested on:
** SunOS 3.5 -ffpa
** SunOS 4.0 -fsoft, -f68881, -f68881 -fstore
** Multiflow 7/200 cc 1.6.2 -checkout, -01, -03
**
** long double is simulated on non-ANSI compilers as register double
*/
#include <math.h>
#include <stdio.h>
/* functions which narrow precision to float and double */
#ifdef __STDC__
float storeff(double x); 
{
   return x;
}


double storef(long double x);
{
   return storeff(x);
} /* narrow to float storage */


double store(long double x);
{
   return x;
} /* force narrowing to double storage */

#else
long  storeff(x)
long  x;
{
   return x;
}


double storef(x)
register double    x;
{
   union {
       float  f;
       long   l;  
   } xx;
   xx.f = x;
   xx.l = storeff(xx.l);
   return (double)xx.f;
}


double store(x)
register double    x;
{
   return x;
} /* force narrowing to double storage */


#define SEEK_SET OL
#endif
#define wp854(radix,precis) ((int)(log(radix)/log(10.)*precis+2))
/* calculate width in decimal digits according to IEEE P854 */
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define ODD(i) ((i)&1) /* like Pascal */
#include <ctype.h> /* check what's there, it may not be too swift */

double atof(dstr) char *dstr; /* any correctly working atof() may be used */
{
   int c,
   /* difference from exponent of accumulated integer to final value */
   exp = 0,

   neg_val = 0,/* leading negative sign ? */
   e_exp = 0, /* number after e/E +/- */
   neg_exp = 0;/* - after E/e ? */
#ifdef __STDC__
   long double x,retval = 0;
#else
   register double x,retval = 0;
#endif
   while (isspace(c = *dstr)) /* skip leading space */
       dstr++;
   switch (c) { /* optional sign */
   case '-':
       neg_val=1;
   case '+': /* fall-through */
       dstr++;
   }

   while (isdigit(c = *dstr++)) {
       /* add up converted values before dec point */
       retval = 10 * retval + (double)(c - '0');
   }
   if ( c == '.')
       while (isdigit(c = *dstr++)) {
           --exp; /* and after dec pt */
           retval = 10 * retval + (double)(c - '0');
       };
   if ((c | ' ')== 'e' ) { /* found an exponent lead-in */

       switch (*dstr) { /* sign field */
       case '-':
           neg_exp = 1;
       case '+': /* fall-through */
       case ' ': /* many FORTRAN environments generate this! */
           dstr++;
       default:
           ; /* fall-through */
       }
       if (isdigit(c = *dstr)) /* found exponent digit */
           do
             e_exp = 10 * e_exp + c - '0';
           while (isdigit(c = *++dstr));
   }
   if (!neg_exp) {
      /* we count on pow having a positive integer power case;
** a production shared library would use exp as an index into a table
** of powers of 10, but we don't know the size of the table yet;
** to reduce error and keep down the table size, we would have only
** positive powers, but partially underflowed numbers would require
** 2 steps */
       if ((exp += e_exp) > O)
           retval *= pow(10., (double)exp);
       else if (exp < 0 ) /* don't introduce extra error */
           retval /= pow(10., (double)(-exp));
   } else { /* this should avoid unnecessary underflow */
       if(ODD(exp = e_exp-exp))retval /= 10;
       for(x=10;exp>1;retval /= x)
           do
              x *= x; /* generate 100,10000,... */
           while(!ODD(exp >>= 1));
   }
   return (neg_val ? -retval : retval); /* apply sign */
}
#ifdef __STDC__
long double    cvtest(x, wpi)
int    wpi; /* ieee width */
long double    x;
{ /* find error in conversion to dec and back */
   char dec[50];
   sprintf(dec, "%.*Lg", wpi, x);
   return atof(dec);
}
#else
double cvtest(x, wpi)
int    wpi; /* ieee width */
double x;
{
   char dec[50];
   sprintf(dec, "%.*g", wpi, x);
   return atof(dec);
}


#endif
/* begin main */
main()
{
   FILE * stream; /* file handle for "float.h" output */
   long    overfpos;/* position in file for DBL_MAX */
   /* internal results: subtraction guarded? widths associated with precison */
   int subgrd, count, precis, wpd, wpf, wpl;
   /* internal results corresponding to _RADIX, _EPSILON, _EPSILON/RADIX, 1/_EPSILON */
   double radix, ulpmd, ulppd, ulpmf, ulppf, ulppl, ulpml, w;
   /* internal variables in largest accessible precision */
#ifdef__STDC__
   long double y, z, v, sat;
#else
   register double y, z, v, sat;
#endif
   /*
**
** RADIX */
   double y1, y2, h, v1[16], v2[16]; /* internal test values */
   stream = fopen("float.h", "w+");
   /* find smallest power of 2 which is unaffected by adding 1 */
   w = 1;
   do
       w += w;
   while (store(w + 1) - w >= 1);
   /* radix: smallest amount by which w may be increased */
   y = 1;
   while (!(radix = store(w + (y += y)) - w))/* stop as soon as non-zero */
       ;
   /* assume radix is same regardless of precision */
   fprintf(stream, "#define FLT_RADIX\t%d\n", (int)radix);
   /*
**
**  PRECISION */
   w = 1;
   precis = 0;
   do {
       ++precis;
       w *= radix;
   } while (store(w + 1) - w == 1); /* stop when error seen */
   ulppd = (ulpmd = 1 / w) * radix;
   wpd = wp854(radix, precis); /* corresponding decimal digits with guard */
   fprintf(stream,
       "#define DBL_EPSILON\t%.*g\n#define DBL_DIGITS\t%d\n#define DBL_MANT_ DIG\t%d\n",
       wpd, ulppd, (int)ceil(-log(ulppd) / log(10.)),precis);
   w = 1;
   precis = 0;
do {
   ++precis;
   w *= radix;
} while (storef(w + 1) - w == 1);
ulppf = (ulpmf = 1 / w) * radix;
wpf = wp854(radix, precis);
fprintf(stream,
   "#define FLT_EPSILON\t%.*g\n#define FLT_DIGITS\t%d\n#define FLT_MANT_DIG\t%d\n",
   wpf, ulppf, (int)ceil(-log(ulppf) / log(10.)),precis);
w = 1;
precis = 0;
do {
   ++precis;
   z = (w *= radix) + 1;
} while (z - w == 1);/* try to force order of operations */
ulppl = (ulpml = 1 / w) * radix;
wpl = wp854(radix, precis);
fprintf(stream,
   "#define LDBL_EPSILON\t%.*g\n#define LDBL_DIGITS\t%d\n#define LDBL_MANT_DIG\t%d\n",
   wpl, ulppl, (int)ceil(-log(ulppl) / log(10.)),precis);
/* ** MULTIPLICATION ROUNDING */
   /* multiplication rounding test not ANSI-defined
**       all rounding tests based on highest available precision
**       and thus will vary with compiler options which change precision
** some known results:
** 68881 (Sun 4.0)
**   multiply and divide are rounded in register precision, but compiler
**   may round to double instead under certain circumstances, including
**   -fstore, they are rounded in double. add/subtract are rounded in
**   register precision but suffer from    double rounding in double storage
** 8087
**   all double operations suffer from double rounding
**   register precision not accessible with most compilers
** Multiflow
**   operations are rounded, but compiler converts v[]/y to v[]*1/y
**   which suffers from double rounding
**   -checkout (fc 1.6.2) fouls up addition rounding test
** Convex
**   compiler reorders operations, often losing 1 bit of precision
** IBM 360 architecture
**   all operations are chopped
** Cray-like architectures (including Convex)
**   vector/scalar division suffers from double rounding
**   or the pre-inversion itself may suffer from multiple rounding
*/
   z = ulpml;
   y =  ulppl;
   /* check for adequate guards to permit round up */
   if (!(y1 = (1 + y) * (1 - y) - 1) & !(y2 = (1 + y + y) * (1 - y) - 1 - y))
       fprintf(stream, "#define MUL_ROUNDS\t1\n");
   else if (y1 < 0 & y2 == -y)
       fprintf(stream, "#define MUL_ROUNDS\t0\n");
   else
       fprintf(stream, "#define MUL_ROUNDS\t-1\n");
/*
** DIVISION ROUNDING */
   /* test rounding of division, not ANSI C required */
   for (count = -1; ++count < 16; ) { /* initialize vectors where loops cannot combine */
       v1[count] = 1 - ulpmd - ulpmd;
       v2[count] = 1 + ulppd + ulppd;
   }/* now do register rounding tests */
   if (!(y1 = (1 - z - z) / (1 - z) - 1 + z) & !(y2 = (1 + y + y) / (1 + y) - 1 - y) )
       fprintf(stream, "#define DIV_ROUNDS\t1\n");
   else if (y1 < 0 & y2 < 0)
       fprintf(stream, "#define DIV_ROUNDS\t0\n");
   else
       fprintf(stream, "#define DIV_ROUNDS\t-1\n");
/*
** VECTOR BY SCALAR DIVISION */
   /* results may depend on compiler options
** same tests as above but memory to memory double arithmetic */
   y1 = 1 - ulpmd;
   y2 = 1 + ulppd;
   for (count = -1; ++count < 16; ) {
       v1[count] /= y1;
       v2[count] /= y2;
   }
   if (!(y1 = v1[0] - y1) & !(y2 = v2[0] - y2) )
       fprinf(stream, "#define VECTOR_BY_SCALAR_DIV_ROUNDS\t1\n");
   else if (y1 < 0 & y2 < 0)
       fprintf(stream, "#define VECTOR_BY_SCALAR_DIV_ROUNDS\t0\n");
   else
       fprintf(stream, "#define VECTOR_BY_SCALAR_DIV_ROUNDS\t-1\n");
   /*
**
** ADD/SUBTRACT ROUNDING -- ANSI REQUIRED */
   /* find out if add/subtract are guarded; put in file ? */
   /* K&R C allows cheating here but it probably doesn't matter
** unless the compiler fouls up like Convex which forms (1+z)-1
** resulting in z*2 thus calculating subgrd == 0 */
   subgrd = 1 - (1 - z) == z & radix - (radix - y) == y;
   /* test rounding of add/subtract with expressions close to threshold
** If rounding test fails, it may mean that the rounding threshold
** varies, as with fast software floating point, or it could be double
** rounding as on 8087 and 68881 */
   if (1 + y * (1 - y) == 1 & 1 - z * z == 1 - z)
/* much more detailed tests would be needed to distinguish FLT_ROUNDS == 3
** for obsolete machines which switch from 0 to 3 at -1 or -.5*/
              fprintf(stream, "#define FLT_ROUNDS\t0\n");
   else if (1 + y == 1 + (y + .5) * y & 1 == 1 + (.5 - y) * y &
       1 - z == 1 - (y + .5) * z & 1 == 1 - (.5 - y) * z & subgrd)
       fprintf(stream, "#define FLT_ROUNDS\t1\n");
   else
       fprintf(stream, "#define FLT_ROUNDS\t-1\n");
/*
** MINIMUM NORMALIZED VALUES */
   /* test for partial underflow, turn off trapping */
   z = (h = 1 / radix) * (1 + ulppd);
   /* loop, dividing by radix, until irreversible result obtained
** there are ways to make this much faster */
   do
       y = z;
   while (store(z *= h) * radix == y);
   /* if conversion errors, adjust away from threshold
** using precautions to avoid abrupt underflow on non-IEEE machine */
   y *= fabs(cvtest(y * radix, wpd) / (y * radix) - 1) + 1;
   fprintf(stream,
       "#define DBL_MIN\t\t%.*g\n#define DBL_MIN_10_EXP\t%d\n#define DBL_MIN_EXP\t%d\n",
       wpd, y, (int)(log(y) / log(10.)),(int)(log(y)/log(radix)+.5));
   z = h * (1 + ulppf);
   do
       y = z;
   while (storef(z *= h) * radix == y);
   y *= fabs(cvtest(y * radix, wpf) / (y * radix) - 1) + 1;
   fprintf(stream,
       "#define FLT_MIN\t\t%.*g\n#define FLT_MIN_10_EXP\t%d\n#define FLT_MIN_EXP\t%d\n",
       wpf, y, (int)(log(y) / log(10.)),(int)(log(y)/log(radix)+.5)); 
   z = h * (1 + (y = ulppl));
   /* count used to obtain log base radix in case log() would underflow */
   count = 0;
   do {
       ++count;
       y = z;
   } while ((z *= h) * radix == y);
   y *= fabs(cvtest(y * radix, wpl) / (y * radix) - 1) + 1;
   /* K&R libraries may fail to display meaningful values */
#ifdef_STDC__
   fprintf(stream,
       "#define LDBL_MIN\t%.*Lg\n#define LDBL_MIN_10_EXP\t%d\n#define LDBL_MIN_EXP\t%d\n",
#else
       fprintf(stream,
       "#define LDBL_MIN\t%.*g\n#define LDBL_MIN_10_EXP\t%d\n#define LDBL_MIN_EXP\t%d\n",
#endif
       wpl, y, (int)((1-count) / log(10.) * log(radix) ),1-count);
/*
** MAXIMUM NORMALIZED VALUES */
   /* keep overwriting overflow threshold in case of abort, but turn off
   trapping if possible */
   overfpos = ftell(stream);
       z = radix * (y = -radix); /* - numbers may work best for 2's complement */
   /* loop until multiplying by radix doesn't make it more negative */
   do {
       v = y;
           if (fseek(stream, overfpos, SEEK_SET))
           printf("seek failure in overflow search\n");
           fprintf(stream,
           "#define DBL_MAX\t\t%.17g\n#define DBL_MAX_10_EXP\t%d\n#define DBL_MAX_EXP\t%d",
           -z, (int)(log(-z) / log(10.)),(int)(log(-z)/log(radix)+.5));     -
           if (fflush(stream))printf("flush failure in overflow search\n");
           z = store((y = z) * radix);        } while (z < y);
       sat = (-y);
       if ((z = (y = v * (radix * ulppd - radix)) + v * ((1 - radix) * ulppd)) < sat)y=  z;
       if (y < sat)v = y;
       if (sat - fabs(v) < sat)v = sat;
      /* if conversion errors, adjust away from threshold */
      v -= fabs(cvtest(v * .75, wpd) - v * .75) * radix;
      if (fseek(stream, overfpos, SEEK_SET))
      printf("seek failure after overflow search\n");
      fprintf(stream,
      "#define DBL_MAX\t\t%.*g\n#define DBL_MAX_10_EXP\t%d\n#define DBL_MAX_EXP\t%d\n",
      wpd, v, (int)(log(v) / log(10.) ),(int)(log(v)/log(radix)+.5)); 
      fprintf(stream, "#ifndef HUGE_VAL\n#define HUGE_VAL\t%g\n#endif\n", sat);
      z = radix * (y = -radix);
      do {
             v = y;
          z = storef((y = z) * radix);       } while (z < y);
      sat = (-y);
      if ((z = (y = v * (radix * ulppf - radix)) + v * ((1 - radix) * ulppf)) < sat)y=  z;
      if (y < sat)v = y;
      if (sat - fabs(v) < sat)v = sat;
      v -= fabs(cvtest(v * .75, wpf) - v * .75) * radix;
      fprintf(stream,
      "#define FLT_MAX\t\t%.*g\n#define FLT_MAX_10_EXP\t%d\n#define FLT_MAX_EXP\t%d\n",
      wpf, v, (int)(log(v) / log(10.) ),(int)(log(v)/log(radix)+.5));
      z = radix * (y = -radix);
      count = -1;
      do {
                 ++count;
          v = y;
          z = (y = z) * radix;      } while (z < y);
      sat = (-y);
      if ((z = (y = v * (radix * ulppl - radix)) + v * ((1 - radix) * ulppl)) < sat)y = z;
      if (y < sat) v = y;
      if (sat - fabs(v) < sat) {
      ++count;
                 v = sat;       }
          v -= fabs(cvtest(v * .75, wpl) - v * .75) * radix;
      /* K&R libraries may fail to display meaningful values */
#ifdef __STDC__
   fprintf(stream,
      "#define LDBL_MAX\t%.*Lg\n#define LDBL_MAX_10_EXP\t%d\n#define LDBL_MAX_EXP\t%d\n",
#else
      fprintf(stream,
"#define LDBL_MAX\t%.*g\n#define LDBL_MAX_10_EXP\t%d\n#define LDBL_MAX_EXP\t%d\n",
#endif
      wpl, v, (int)((count+2) * log(radix) / log(10.) ),count+2);
      exit(0); }