
Listing 4 -- xstod.c


/* _Stod function */
#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <locale.h>
#include <stdlib.h>
#include "xmath.h"

#define SIG_MAX	32

double _Stod(const char *s, char **endptr)
	{	/* convert string to double, with checking */
	const char point = localeconv()->decimal_point[0];
	const char *sc;
	char buf[SIG_MAX], sign;
	double x;
	int ndigit, nsig, nzero, olead, opoint;

	for (sc = s; isspace(*sc); ++sc)
		;
	sign = *sc == '-' || *sc == '+' ? *sc++ : '+';
	olead = -1, opoint = -1;
	for (ndigit = 0, nsig = 0, nzero = 0; ; ++sc)
		if (*sc == point)
			if (0 <= opoint)
				break;	/* already seen point */
			else
				opoint = ndigit;
		else if (*sc == '0')
			++nzero, ++ndigit;
		else if (!isdigit(*sc))
			break;
		else
			{	/* got a nonzero digit */
			if (olead < 0)
				olead = nzero;
			else	/* deliver zeros */
				for (; 0 < nzero && nsig < SIG_MAX; --nzero)
					buf[nsig++] = 0;
			++ndigit;
			if (nsig < SIG_MAX)	/* deliver digit */
				buf[nsig++] = *sc - '0';
			}
	if (ndigit == 0)
		{	/* set endptr */
		if (endptr)
			*endptr = (char *)s;
		return (0.0);
		}
	for (; 0 < nsig && buf[nsig - 1] == 0; --nsig)
		;	/* skip trailing digits */
	 {	/* compute significand */
	const char *pc = buf;
	int n;
	long lo[SIG_MAX/8+1];
	long *pl = &lo[nsig >> 3];
	static double fac[] = {0, 1e8, 1e16, 1e24, 1e32};

	for (*pl = 0, n = nsig; 0 < n; --n)
		if ((n & 07) == 0)	/* start new sum */
			*--pl = *pc++;
		else
			*pl = *pl * 10 + *pc++;
	for (x = (double)lo[0], n = 0; ++n <= (nsig >> 3); )
		if (lo[n] != 0)
			x += fac[n] * (double)lo[n];
	 }
	 {	/* fold in any explicit exponent */
	long lexp = 0;
	short sexp;

	if (*sc == 'e' || *sc == 'E')
		{	/* parse exponent */
		const char *scsav = sc;
		const char esign = *++sc == '+' || *sc == '-'
			? *sc++ : '+';

		if (!isdigit(*sc))
			sc = scsav;	/* ill-formed exponent */
		else
			{	/* exponent looks valid */
			for (; isdigit(*sc); ++sc)
				if (lexp < 100000)	/* else overflow */
					lexp = lexp * 10 + *sc - '0';
			if (esign == '-')
				lexp = -lexp;
			}
		}
	if (endptr)
		*endptr = (char *)sc;
	if (opoint < 0)
		lexp += ndigit - nsig;
	else
		lexp += opoint - olead - nsig;
	sexp = lexp < SHRT_MIN ? SHRT_MIN : lexp < SHRT_MAX
		? (short)lexp : SHRT_MAX;
	x = _Dtento(x, sexp);
	return (sign == '-' ? -x : x);
	 }
	}

