/*[]------------------------------------------------------------[]*/
/*|                                                              |*/
/*|     isteldbl.cpp                                             |*/
/*|                                                              |*/
/*|     Class istream                                            |*/
/*|          istream& istream::operator>> ( long double& )       |*/
/*|                                                              |*/
/*[]------------------------------------------------------------[]*/

/*
 *      C/C++ Run Time Library - Version 5.0
 *
 *      Copyright (c) 1990, 1992 by Borland International
 *      All Rights Reserved.
 *
 */

/*
 * If you have an optimized strtod() routine (or atof() or equivalent),
 * you may wish to extract characters into a local string and call
 * strtod() with that string instead of using the code given here.
 *
 * We take care to avoid floating-point overflow and underflow during all
 * calculations and set errno to ERANGE on overflow and underflow, but
 * we do not raise any error signals.
 * You can speed up the code by removing the checks if they don't matter
 * in your system.
 *
 * This code uses type long double exclusively, under the assumption
 * that the math chips and the software floating-point library all
 * convert all floats and doubles to long double internally.  It makes
 * more sense to do this conversion once on input and once on output
 * rather than multiple times.
 */

#include <ioconfig.h>
#include <iostream.h>
#include <ctype.h>
#include <float.h>  // for characteristics of floating types
#include <math.h>      // for HUGE_VAL
#include <errno.h>  // for reporting range errors



/*
 * The following defines are for type long double.
 * If you want to do float or long separately,
 * you will need to supply the appropriate values
 */
#define Dbigexp 49320L      // much bigger than any power 10 exponent
#define Dmaxint 493200L     // (10*Dbigexp)
#define Dfixexp 2466L       // about max_10_exp / 2
#define Dfixval 1.0e2466    // 10 ** Dfixexp


istream _FAR & istream::operator>> (long double _FAR & val)
{
    if( ipfx0() )
        {     // verify file state and possibly skip white

        int c;          // current input character
        int negval;     // entire value is negative
        int negexp;     // exponent is negative
        long exp;       // accumulated exponent
        int formok;     // floating-point number format is valid

        formok = 0;     // until proven otherwise
        c = bp->sgetc();    // look at first character

        // get leading sign
        negval = c == '-';
        if( c == '+'  ||  negval )
            c = bp->snextc();
        else if( c == EOF )
            setstate(ios::badbit);  // no characters extracted

        // scan off digits up to decimal point
        val = 0.0;
        if( isdigit(c) )
            {
            formok = 1;
            do  {
                c -= '0';
                val = 10.0 * val + c;
                c = bp->snextc();
                } while( isdigit(c) );
            }

        // scan off fractional digits, keeping count
        exp = 0;
        if( c == '.' )
            {
            c = bp->snextc();
            if( isdigit(c) )
                {
                formok = 1;
                do  {
                    c -= '0';
                    val = 10.0 * val + c;
                    --exp;
                    c = bp->snextc();
                    } while( isdigit(c) );
                }
            }

        // look for possibly-signed exponent
        if( formok  &&  (c == 'e'  ||  c == 'E') )
            {
            c = bp->snextc();
            formok = isdigit(c)  ||  c == '+'  ||  c == '-';

            long givenexp = 0;  // exponent given in E... part of number
            if( ! formok )
                val = 0.0;
            else
                {
                // convert possibly-signed exponent
                negexp = c == '-';  // here we mean the given exponent
                if( c == '+'  ||  negexp )
                    c = bp->snextc();
                formok = isdigit(c);
                if( ! formok )
                    val = 0.0;
                else
                    {
                    do  {
                        if( givenexp < Dbigexp )
                            {
                            c -= '0';
                            givenexp = 10 * givenexp + c;
                            }
                        c = bp->snextc();
                        } while( isdigit(c) );
                    }
                }

            // adjust exponent by given exponent, watching for overflow
            if( negexp )
                {
                if( givenexp - Dmaxint < exp )
                    exp -= givenexp;
                else
                    exp = - Dmaxint;
                }
            else
                {
                if( Dmaxint - givenexp > exp )
                    exp += givenexp;
                else
                    exp = Dmaxint;
                }
        }

    // try to salvage some underflow
    if( exp <= LDBL_MIN_10_EXP )
        {
        val /= Dfixval;
        exp += Dfixexp;;
        }

    // final calculation
    if( exp < LDBL_MIN_10_EXP )
        {
        val = 0.0;
        errno = ERANGE;
        }
    else if( exp > LDBL_MAX_10_EXP )
        {
        val = HUGE_VAL;
        errno = ERANGE;
        }
    else if( formok )
        {
        int overflow = 0;           // did overflow occur
        long double adjust = 1.0;   // amount to adjust because of exponent
        long double power = 10.0;   // used in building adjust
        negexp = exp < 0;           // negexp now refers to net exponent
        if( negexp )
            exp = -exp;     // exp is now absolute value

        // compute val * (10 ** exp)
        while( exp > 0 )
            {
            if( exp & 1 )
                {
                if( LDBL_MAX / adjust >= power )
                    adjust *= power;
                else
                    overflow = 1;
                }
            exp >>= 1;
            if( exp > 0 )
                {
                if( LDBL_MAX / power >= power )
                    power *= power;
                else
                    overflow = 1;
                }
            }

        if( ! overflow )
            {
            if( adjust != 1.0 )
                {
                if( negexp )
                    val /= adjust;
                else if( val <= 1.0 )
                    val *= adjust;
                else if( LDBL_MAX / val >= adjust )
                    val *= adjust;
                else
                    overflow = 1;
                }
            }
        if( overflow )
            {
            if( negexp )
                val = 0.0;
            else
                val = HUGE_VAL;
            errno = ERANGE;
            }
        }

    if( formok )
        {
        if( negval )
            val = -val; // adjust the sign
        }
    else if( c == EOF)
        setstate(ios::eofbit | ios::failbit);
    else
        setstate(ios::failbit);
    }

    return *this;
}
