Beispiel #1
0
static void __scanttod (
                        long double *valueP,
                        int  (* Get) (void *srceP),
                        void (* UnGet) (int ch, void *srceP),
                        void  *srceP,
                        int     width,
                        int    *countP,
                        int    *statusP )
{
	int     decimals = 0x8000;
	int     digits = -2;
	int     maxdigits = MAX_IEEE_DIGS;  /* try 19 at first */
	int     exponent;
	char    sign    = 0;
	char    FirstDigit = 1;
	char    saw_sign= 0;
	char    expSign = 0;
	char    ExpOflow= 0;
	int     ct      = 0;
	int     status  = 1;
	int     ch;

	long double frac = 0.0;
	long double exp;
	unsigned short  *fracw = (unsigned short *)&frac;
	unsigned long   *fracl = (unsigned long  *)&frac;
	long            tempq[2];
	_TCHAR          *decimalpoint = (_TCHAR*) _getLocaleNumericInfo(LOCALE_SDECIMAL);
	_TCHAR          decimal_point_tchar = *decimalpoint; //*_localeconvention.decimal_point;

    /* Skip leading spaces on the input numeral.
     */
    for (;;)
    {
        ct++;
        if ((ch = Get (srceP)) == EOF)
        {
            status = -1;
            goto std_noResult;
        }
#ifndef _UNICODE
	if ((ch & 0x80) != 0 || !_istspace(ch))
#else
	if (!_istspace((int)ch))
#endif
	    break;
    }

    if (--width < 0)
        goto std_fractionLimited;

    /* Is the numeral preceded by a sign ?
     */
    if (ch == _TEXT('+'))
        saw_sign = 1;
    else if (ch == _TEXT('-'))
        sign = saw_sign = 1;
    else
        goto std_gotChar;

    for (;;)            /* Pick up the next character of the fraction.  */
    {
        if (--width < 0)
            goto std_fractionLimited;
        ct ++;
        ch = Get (srceP);

        /* Check for the special cases where +INF -INF +NAN -NAN
         * might be specified.
         */
std_gotChar:
        if (FirstDigit == 1 && saw_sign != 0)
        {
            if (ch == _TEXT('I'))       /* Maybe we have +/-INF         */
                goto PossibleINF;
            else if (ch == _TEXT('N'))  /* Maybe we have +/-NAN         */
                goto PossibleNAN;
        }

        FirstDigit = 0;

        /* Watch for decimal points.
         */
        if (ch == decimal_point_tchar )
        {
            if (decimals != 0x8000)     /* Seen a previous decimal point? */
                break;                  /* If so, fraction is terminated */
            decimals = digits > 0 ? digits : 0;
            continue;
        }

        if (ch < _TEXT('0') || ch > _TEXT('9'))
            break;                     /* Non-numeric terminates fraction */
        ch -= _TEXT('0');              /* convert digit to equivalent number.  */

        if (++digits <= 0)             /* Was it the first digit? */
        {
            /* The first digit begins the fraction.  Leading
             * zeros are noted by setting digits -1, so that the
             * fraction syntax is valid if no other digits are
             * seen, but following digits will still be treated
             * as "firsts".  Leading non-zero digits cause digits
             * to be set to 1.
             */
            fracw[0] = ch;
            if (ch != 0)                /* not a leading zero? */
                digits = 1;
            else
            {
                digits = -1;            /* it is a leading zero */
                if (decimals != 0x8000) /* has decimal point been seen ? */
                    decimals--;         /* if yes, move it to the left. */
            }
            continue;
        }

        /* If a digit is seen, then multiply the existing fraction by 10 and
         * add in the new digit.  The special case of the first 9 digits is
         * treated separately for speed, because the value can't exceed
         * 32 bits.
         */
        if (digits <= 9)
        {
            *fracl = *fracl * 10L + (long)ch;
        }
        else if (digits <= maxdigits)
        {
            /* Digits beyond the 9th are more rare in practice
             * (even in 10-digit numbers, 9 will be quick), so no
             * further special cases are justified.  Beyond MAX_IEEE_DIGS
             * digits, or beyond 2^64, ignore the digit values
             * but keep scanning.
             */
            tempq[0] = fracl[0];            /* make temporary copy of frac */
            tempq[1] = fracl[1];
            if (_qmul10(tempq, ch) != 0)    /* overflow? */
                maxdigits = digits -1;      /* say we never saw this digit */
            else
            {
                fracl[0] = tempq[0];        /* no overflow, copy temporary */
                fracl[1] = tempq[1];        /*  result to frac */
            }
        }
        continue;

        /* Arrive here when fraction is width-limited but valid.
         */
std_fractionLimited:
        ch = _TEXT('e');                   /* Behave as if exponent started. */
        break;
    }

    /* The fraction was ended.  If it was valid, it must have had at least
     * one digit.  AL must hold the character which terminated the fraction.
     */
    if (digits == -2)
        goto std_noDigitSeen;

    /* If no decimal point was seen, then the decimal is assumed to be at
     *  the rightmost edge.
     */
    if (decimals == 0x8000)
        decimals = digits;

    /* Now we must gather the exponent.  First, check for 'E' or 'e' to
     * introduce it, then if found gather the short integer.
     */
    exponent = 0;
    if (ch == _TEXT('e') || ch == _TEXT('E'))
    {
        int SignStage = 1;

        for (;;)
        {
            if (--width < 0)
                goto std_exponentLimited;
            ct ++;
            ch = Get (srceP);
            if (SignStage)
            {
                SignStage = 0;
                if (ch == _TEXT('-'))
                {
                    expSign = 1;
                    continue;
                }
                else if (ch == _TEXT('+'))
                    continue;
            }
            if (ch < _TEXT('0') || ch > _TEXT('9'))
                break;
            if ((exponent = exponent * 10 + ch - _TEXT('0')) > 4932)
                ExpOflow = 1;
        }
    }

    /* Arrive here when a valid syntax has been terminated
     * ch must still contain the terminating character, unchanged
     */
    UnGet (ch, srceP);
    ct--;

    /* Arrive here with valid termination but no terminator to be pushed back.
     */
std_exponentLimited:
    if (expSign)                /* was the exponent signed ?    */
    {
        exponent = -exponent;

        /* Normal stays normal, Infinity becomes 0 if exponent was hugely
         * negative.
         */
        ExpOflow = -ExpOflow;
    }

    /* The special case when digits = -1 occurs when the fraction is zero.
     * In that case, the result is always zero, whatever the exponent.
     */
    if (digits < 0)
    {
        frac = 0.0;
        goto std_end;
    }

    /* Convert underflows to zero and overflows to ld HUGE_VAL.
     */
    if (ExpOflow)
    {
        if (ExpOflow == 1)              /* big (+) exp -> ld HUGE_VAL */
        {
            /* Make 'frac' a long double HUGE_VAL.
             */
            fracw[0] = fracw[1] = fracw[2] = fracw[3] = 0xffff;
            fracw[4] = 0x7ffe;
        }
        else
            frac = 0.0;
        status = 2;
        goto std_end;
    }

    /* Add the decimal point contribution to the exponent.
     */
    exponent += decimals - (digits > maxdigits ? maxdigits : digits);
    frac = _fuildq((long *)&frac);  /* Convert fraction to a long double */

    /* Multiply fraction * 10^exponent if exponent is non-zero
     */
    if (exponent != 0)
    {
        exp = _pow10(exponent > 0 ? exponent : -exponent);
        if (exponent < 0)       /* negative exponent --> 1 / 10^|exponent|  */
            frac = frac / exp;
        else
            frac = frac * exp;
    }

std_end:
    if (sign)
        frac = -frac;

std_exit:
    *countP += ct;
    *statusP = status;
    *valueP = frac;
    return;

/* Error clauses placed here out of the main loop.
 * Arrive here if an error occurred.
 */
std_noDigitSeen:
    status = 0;

std_noResult:
    if (width >= 0)
    {
        UnGet (ch, srceP);
        ct --;
    }
    frac = 0.0;
    goto std_end;

/*  end of error clauses.
 */

/*-------------------------------------------------------------------------
                Special case code to scan +INF -INF +NAN -NAN
  -------------------------------------------------------------------------
  One side effect here, if this ultimately isn't +INF -INF +NAN -NAN will be
  that the apps input stream is now messed up because we needed to look
  ahead more than 1 character to recognize INF or NAN. The 'unget' functions
  are only guaranteed to be able to unget a maximum of one char. This means
  on a worst case input like "+INX" there will be 3 characters we won't be
  able to push back on the stream successfully.  There's not much that can
  be done to prevent this.  The same kind of thing can happen when reading
  E format numbers, for example "1.234E+Q".  By the time the 'Q' is seen
  "E+" has gone by.
--------------------------------------------------------------------------*/
PossibleINF:
    ct ++;
    ch = Get (srceP);
    if (--width < 0 || ch != _TEXT('N'))
        goto std_noDigitSeen;
    ct ++;
    ch = Get (srceP);
    if (--width < 0 || ch != _TEXT('F'))
        goto std_noDigitSeen;
    if (sign)
        frac = *(long double *)INFM;
    else
        frac = *(long double *)INF;
    goto std_exit;

PossibleNAN:
    ct ++;
    ch = Get (srceP);
    if (--width < 0 || ch != _TEXT('A'))
        goto std_noDigitSeen;
    ct ++;
    ch = Get (srceP);
    if (--width < 0 || ch != _TEXT('N'))
        goto std_noDigitSeen;
    if (sign)
        frac = *(long double *)NANM;
    else
        frac = *(long double *)NAN;
    goto std_exit;
}
Beispiel #2
0
double
strtod(const char *nptr, char **endptr)
{
	register double value;
	register unsigned int c;
	register const char *endp;
	register int flags, decexp, expexp, digits;
	unsigned long ul;

	/* Initialize flags, result value, end pointer, current character. */
	flags = 0;
	value = 0.0;
	endp = nptr;
	c = (unsigned int)*nptr++;

	/* Ignore initial whitespace. */
	while (isspace(c))
		c = (unsigned int)*nptr++;

	/* Look for leading sign. */
	if (c == '+' || c == '-') {
		if (c == '-')
			flags |= SIGN;		/* negate result */
		c = (unsigned int)*nptr++;
	}
	if (c != '.' && !isdigit(c))
		goto done2;			/* no valid subject string */

	/* Accept a nonempty sequence of digits optionally containing '.'. */
	for (ul = digits = decexp = 0;
			isdigit(c) || (c == '.' && (flags & DOTSEEN) == 0);
			c = (unsigned int)*nptr++) {

		/* Look for decimal point. */
		if (c == '.') {
			flags |= DOTSEEN;	/* note '.' seen */
			if ((flags & DIGITSEEN) != 0)
				endp = nptr;	/* input accepted to here */
			continue;
		}

		/*
		 * c must be a digit at this point.
		 * Compute the significand in an unsigned long ul
		 * until it overflows, counting digits.
		 * The actual current significand is:
		 * 	value * _pow10(digits) + ul
		 */
		flags |= DIGITSEEN;
		endp = nptr;			/* input accepted to here */
		c -= '0';
		if (ul > (ULONG_MAX - 9) / 10) {
			/* The significand in ul is about to overflow. */
			value = ((flags & TOOBIG) != 0) ? value * _pow10(digits) + ul : (double)ul;
			ul = (unsigned long)c;
			digits = 1;
			flags |= TOOBIG;
		} else {
			ul = ul * 10 + c;
			++digits;
		}
		if ((flags & DOTSEEN) != 0)
			--decexp;		/* adjust implicit exponent */
	}

	/* Store the significand in value. */
	value = ((flags & TOOBIG) != 0) ? value * _pow10(digits) + ul : (double)ul;

	/* Look for optional exponent: 'E' or 'e', optional sign, decimal digits. */
	if (c == 'E' || c == 'e') {
		c = (unsigned int)*nptr++;

		/* Exponent sign. */
		if (c == '+' || c == '-') {
			if (c == '-')
				flags |= EXPSIGN;
			c = (unsigned int)*nptr++;
		}
		if (!isdigit(c))
			goto done1;		/* exponent is absent */

		/* Process digits from explicit exponent. */
		for (digits = expexp = 0; isdigit(c); c = (unsigned int)*nptr++) {
			endp = nptr;		/* input accepted to here */
			c -= '0';
			if (digits != 0 || c != 0)
				++digits;	/* count exponent digits */
			expexp = expexp * 10 + c;
		}

		/*
		 * If the explicit exponent has too many significant digits,
		 * it must produce overflow or underflow.
		 */
		if (digits > DBL_EXP_10_DIG) {
			flags |= ((flags & EXPSIGN) != 0) ? UNDERFLOW : OVERFLOW;
			goto done2;
		}

		/* Reconcile the decimal exponent with the explicit exponent. */
		if ((flags & EXPSIGN) != 0)
			decexp -= expexp;
		else
			decexp += expexp;
	}

	/* Reconcile the result with the decimal exponent. */
done1:
	if (decexp < DBL_MIN_10_EXP)
		flags |= UNDERFLOW;		/* exponent underflow */
	else if (decexp > DBL_MAX_10_EXP)
		flags |= OVERFLOW;		/* exponent overflow */
	else if (decexp > 0)
		value *= _pow10(decexp);
	else if (decexp < 0)
		value /= _pow10(-decexp);	/* avoid precision loss on negative exponent */

	/* Done, store result and return. */
done2:
	if (endptr != NULL)
		*endptr = (char *)endp;
	if ((flags & OVERFLOW) != 0 || (flags & UNDERFLOW) != 0) {
		errno = ERANGE;
		value = ((flags & OVERFLOW) != 0) ? HUGE_VAL : 0.0;
	}
	return ((flags & SIGN) != 0) ? -value : value;
}