home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c065 / 2.ddi / CLIB2.ZIP / OSTREAMF.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-07  |  11.2 KB  |  377 lines

  1. /*-----------------------------------------------------------------------*
  2.  * filename - ostreamf.cpp
  3.  * Class ostream member functions for floating-point insertion
  4.  *-----------------------------------------------------------------------*/
  5.  
  6. /*[]------------------------------------------------------------[]*/
  7. /*|                                                              |*/
  8. /*|     Turbo C++ Run Time Library - Version 1.0                 |*/
  9. /*|                                                              |*/
  10. /*|                                                              |*/
  11. /*|     Copyright (c) 1990 by Borland International              |*/
  12. /*|     All Rights Reserved.                                     |*/
  13. /*|                                                              |*/
  14. /*[]------------------------------------------------------------[]*/
  15.  
  16. /*
  17.     This code assumes the IEEE Standard 754-1985 model of floating-point
  18.     numbers, but makes no assumptions about the details of the floating-point
  19.     representation.  There are several IEEE kinds (classes) of number.
  20.     Positive or Negative Infinity result on overflow, and propagate
  21.     as a mathematical infinity does.  Overflow and underflow may or may not
  22.     produce a signal (interrupt).  Operations on infinities may or may not
  23.     produce a signal.
  24.     An undefined operation, such as (0.0 / 0.0) or (0.0 * Infinity),
  25.     produces a special value called Not-A-Number, or NaN.
  26.     Any operation on a NaN results in a NaN.  Production of or operations on
  27.     a NaN may or may not produce a signal (interrupt).
  28.     When a result is smaller in magnitude than the smallest standard value
  29.     but is not zero, it may be represented as a Denormalized number.
  30.     Operations on denormalized numbers may or may not produce a signal.
  31.  
  32.     The IEEE standard recommends that any implementation supply certain
  33.     auxiliary functions to help in working with special values.  These
  34.     recommended functions are in a header file called <fakeieee.h>.
  35.  
  36.     This code uses the following IEEE functions:
  37.     // return the kind of number
  38.     int fpclass( long double );
  39.  
  40.     // return the first value with the sign of the second,
  41.     // even if one is Infinity or NaN, which would otherwise be
  42.     // an illegal operation
  43.     long double fpcopysign( long double, long double );
  44.  
  45.     // return true if -INFINITY < parameter < +INFINITY
  46.     int fpfinite( long double );
  47.  
  48.     // return true if parameter is a NaN
  49.     int fpisnan( long double );
  50.  
  51.     In addition, we generate special strings for +Infinity, -Infinity,
  52.     and NotANumber.  You may wish to generate other strings, or just
  53.     set Infinities to HUGE_VAL and emit them normally.  It is not
  54.     obvious what to do about NaN's in that case.
  55.  
  56.     If your system does not have an <ieee.h> or equivalent header, or
  57.     if your floating-point system does not fit this model, you may as
  58.     an expedient define the macro name _NOIEEE_ immediately below, and
  59.     use a simplified header "fakeieee.h" supplied with this package.
  60.     You will later want to go through the code to provide any needed
  61.     functionality depending on your floating-point model.
  62.  
  63.     Long doubles are not directly supported by this package.  The
  64.     modifications to support them should be obvious and straightforward.
  65.  
  66.     NOTES:
  67.     1.  Function "round" below contains a rounding table which must
  68.     have as many entries as there are significant decimal digits in
  69.     the floating-point representation.  IEEE double-precision provides
  70.     about 16 significant decimal digits.
  71.     2.  Function "eout" below assumes that the maximum exponent of 10
  72.     which can be represented is no bigger than 999.  This may not be
  73.     true for long doubles, and provision for extra digits will be
  74.     needed.
  75.  
  76.   
  77.     This code has now been modified to use type long double exclusively,
  78.     under the assumption that the math chips and the software floating-
  79.     point library all convert all floats and doubles to long double
  80.     internally.  It makes more sense to do this conversion once on input
  81.     and once on output rather than multiple times.
  82. */
  83.  
  84. #define _NOIEEE_
  85.  
  86. #include <iostream.h>
  87. #include <string.h>
  88. #include <math.h>
  89.  
  90. #ifdef _NOIEEE_
  91. #   include "fakeieee.h"
  92. #else
  93. #   include <ieee.h>
  94. #endif
  95.  
  96. static char Infinity[]    = "[+Infinity]";
  97. static char NegInfinity[] = "[-Infinity]";
  98. static char NotANumber[]  = "[NotANumber]";
  99.  
  100. // structure used to pass around the current status of the value
  101. struct float_data {
  102.     int exp;        // the power of 10
  103.     long double remain;    // normalized range [1.0, 10.0), unprinted digits
  104.     int is_neg;        // is this a negative value
  105.     int is_zero;    // is value zero
  106.     int showpoint;    // do we force display of decimal point
  107.     int precision;    // precision to display (number of digits)
  108.     int expchar;    // 'e' or 'E' in scientific format
  109. };
  110.  
  111.  
  112. /*
  113.  * Funny version of frexp() for long doubles which just returns
  114.  * the exponent.  We don't have long double format published in any
  115.  * header, so we just use non-portable magic code to do the job.
  116.  * Some day we may have a real library version of frexpl(), which
  117.  * should then be used.
  118.  */
  119. static int lfrexp(long double x)
  120. {
  121.     // get last 15 bits of the long double
  122.     int res = (((unsigned int*) &x)[4]) & 0x7FFF;
  123.     return (res == 0) ? 0 : res - 0x3FFF;
  124. }
  125.  
  126. //  Normalize representation of floating value as signed magnitude in
  127. //  range [1.0, 10.0) times 10^exp, or as 0.0
  128. static void normalize( long double val, float_data& eval )
  129. {
  130.     eval.is_neg = 0;
  131.     eval.is_zero = 0;
  132.     eval.exp = 0;
  133.  
  134.     switch( fpclass(val) ) {
  135.       case FPSIGNAN:
  136.       case FPQUIETNAN:
  137.     break;                // cannot process NaN's
  138.  
  139.       case FPNEGINF:
  140.     eval.is_neg = 1;
  141.     val = fpcopysign(val, 1.0);    // make into positive infinity
  142.     // fall through
  143.  
  144.       case FPPOSINF:
  145.     break;
  146.  
  147.       case FPNEGZERO:
  148.     eval.is_neg = 1;
  149.     val = 0.0;            // make into non-negative zero
  150.     // fall through
  151.  
  152.       case FPPOSZERO:
  153.     eval.is_zero = 1;
  154.     break;
  155.  
  156.       case FPNEG:
  157.       case FPNEGDENOR:
  158.     eval.is_neg = 1;
  159.     val = -val;            // make positive
  160.     // fall through
  161.  
  162.       case FPPOSDENOR:
  163.       case FPPOS:
  164.     // processing for non-special values
  165.  
  166.     // this calculation is not exact, but we fix it up later
  167.     int exp = lfrexp(val);               // get exponent base 2
  168.     exp = (int) (((long) exp * 2466L) / 8192); // approx exponent base 10
  169.     eval.exp = exp;
  170.  
  171.     if( exp != 0 ) {
  172.         int neg = 0;
  173.         if( exp < 0 ) {
  174.         exp = -exp;
  175.         neg = 1;
  176.         }
  177.         long double power = 1.0;
  178.         long double temp = 10.0;
  179.         do {
  180.         while( exp != 0  &&  (exp & 1) == 0 ) {
  181.             temp *= temp;
  182.             exp >>= 1;
  183.         }
  184.         power *= temp;
  185.         exp--;
  186.         } while( exp != 0 );
  187.         if( neg ) val *= power;
  188.         else val /= power;
  189.     }
  190.  
  191.     // fix up if out of range (we know it can't be >= 10.0)
  192.     if( val < 1.0 ) {
  193.         val *= 10.0;
  194.         eval.exp -= 1;
  195.     }
  196.     break;
  197.     }
  198.  
  199.     eval.remain = val;
  200. }
  201.  
  202.  
  203. // Return the ASCII representation of the next digit of the value,
  204. // adjusting remainder.
  205. static char next_digit( float_data& eval )
  206. {
  207.     int digit;
  208.  
  209.     digit = (int) eval.remain;
  210.     eval.remain = 10.0 * (eval.remain - (long double) digit);
  211.     return digit + '0';
  212. }
  213.  
  214.  
  215. // Generate ASCII digits for value in float_data.
  216. // Return pointer to the end of the string.
  217. static char* flt_out( float_data& eval, char *pt )
  218. {
  219.     int count, exp;
  220. #pragma -eff
  221.     if( fpisnan(eval.remain) ) {
  222.     strcpy(pt, NotANumber);
  223.     pt += strlen(pt);
  224.     }
  225. #pragma .eff
  226.  
  227.     else if( ! fpfinite(eval.remain) ) {
  228.     if( eval.is_neg ) strcpy(pt, NegInfinity);
  229.     else strcpy(pt, Infinity);
  230.     pt += strlen(pt);
  231.     }
  232.     else {
  233.     // integer portion
  234.     exp = eval.exp + 1;    // add 1 to make subsequent tests cheaper
  235.     if( exp <= 0 )
  236.         *(pt++) = '0';
  237.     else 
  238.         for( ;  exp > 0;  exp-- )
  239.         *(pt++) = next_digit(eval);
  240.  
  241.     // possible decimal point
  242.     if( eval.precision  ||  eval.showpoint )
  243.         *(pt++) = '.';
  244.  
  245.     // fraction portion
  246.     for( count = eval.precision;  count > 0;  count-- ) {
  247.         if( exp < 0 ) {
  248.         *(pt++) = '0';
  249.         exp++;
  250.         }
  251.         else *(pt++) = next_digit(eval);
  252.     }
  253.  
  254.     // maybe strip trailing fractional zeros
  255.     if( eval.precision  &&  ! eval.showpoint )  {
  256.         while( pt[-1] == '0' )
  257.         --pt;
  258.         if( pt[-1] == '.' )
  259.         --pt;    // don't leave a bare decimal point
  260.     }
  261.  
  262.     *pt = '\0';
  263.     }
  264.  
  265.     return pt;
  266. }
  267.  
  268.  
  269. // "digits" = normalized fraction digits affected by rounding.
  270. static void round( float_data& eval, int digits )
  271. {
  272.     // roundamt[n] == 1/2 of nth digit
  273.     const int sigdig = 20;    // see note in introduction
  274.     static long double roundamt[sigdig+1] =
  275.     { 5.0e-1,  5.0e-2,  5.0e-3,  5.0e-4,  5.0e-5,  5.0e-6,
  276.       5.0e-7,  5.0e-8,  5.0e-9,  5.0e-10, 5.0e-11, 5.0e-12,
  277.       5.0e-13, 5.0e-14, 5.0e-15, 5.0e-16, 5.0e-17, 5.0e-18,
  278.       5.0e-19, 5.0e-20, 5.0e-21
  279.     };
  280.  
  281.     switch( fpclass(eval.remain) ) {
  282.       case FPSIGNAN:
  283.       case FPQUIETNAN:
  284.       case FPNEGINF:
  285.       case FPNEGZERO:
  286.       case FPPOSZERO:
  287.       case FPPOSINF:
  288.     break;        // no rounding needed or possible
  289.  
  290.       default:        // finite value may require rounding
  291.     if( 0 <= digits  &&  digits <= sigdig ) {
  292.         eval.remain += roundamt[digits];
  293.         if( eval.remain > 10.0 ) {
  294.         eval.remain /= 10.0;
  295.         eval.exp++;
  296.         }
  297.     }
  298.     break;
  299.     }
  300. }
  301.  
  302.  
  303. // generate a number in scientific format
  304. // if values greater than 10^999 are possible, see note in introduction
  305. static void eout( float_data& eval, char* buf )
  306. {
  307.     // rounding can affect exponent, so do it first
  308.     round(eval, eval.precision);
  309.  
  310.     // save exponent data
  311.     int negexp = 0;        // is exponent negative
  312.     int exp = eval.exp;        // absolute value of exponent
  313.     if( exp < 0 ) {
  314.     negexp = 1;
  315.     exp = -exp;
  316.     }
  317.  
  318.     // generate digits field
  319.     eval.exp = 0;    // for flt_out
  320.     char* pt = flt_out(eval, buf);
  321.  
  322.     if( fpfinite(eval.remain) ) {
  323.     // generate exponent field
  324.     *(pt++) = eval.expchar;
  325.     if( negexp ) *(pt++) = '-';    // always a sign
  326.     else *(pt++) = '+';
  327.     if( exp >= 1000 ) {
  328.         *(pt++) = '0' + exp / 1000;    // fourth digit needed
  329.         exp %= 1000;
  330.     }
  331.     if( exp >= 100 ) {
  332.         *(pt++) = '0' + exp / 100;    // third digit needed
  333.         exp %= 100;
  334.     }
  335.     *(pt++) = '0' + exp / 10;    // at least two digits
  336.     *(pt++) = '0' + exp % 10;
  337.     *(pt++) = '\0';
  338.     }
  339. }
  340.  
  341.  
  342. ostream& ostream::operator<< (long double d)
  343. {
  344.     float_data eval;
  345.     char buf[60];    // big enough for any floating result
  346.  
  347.     normalize(d, eval);
  348.     eval.expchar = (flags() & ios::uppercase) ? 'E' : 'e';
  349.     eval.precision = precision();
  350.     if( eval.precision <= 0 )
  351.     eval.precision = 6;    // default value for precision
  352.     eval.showpoint = (flags() & ios::showpoint) != 0;
  353.  
  354.     if( flags() & ios::fixed ) {
  355.     round(eval, eval.exp + eval.precision);
  356.     flt_out(eval, buf);
  357.     }
  358.     else if( flags() & ios::scientific )
  359.     eout(eval, buf);
  360.     else if( eval.exp < -4  ||  eval.precision < eval.exp )
  361.     eout(eval, buf);    // default to scientific
  362.     else {            // default to fixed
  363.     round(eval, eval.exp + eval.precision);
  364.     flt_out(eval, buf);
  365.     }
  366.  
  367.     char* prefix = 0;
  368.     if( eval.is_neg )
  369.     prefix = "-";
  370.     else if( ! eval.is_zero  &&  (flags() & ios::showpos) )
  371.     prefix = "+";
  372.  
  373.     // now we have a formatted string for output, to be possibly padded
  374.     outstr(buf, prefix);
  375.     return *this;
  376. }
  377.