/*
 *			a t o f . c
 *
 * Ascii to floating
 */

#ifndef decus
#include <ctype.h>
#endif

static double twoe56 = 72057594037927936.;	/* 2**56		*/

static double exp5[] = {
	5.,				/* 5 ^  1			*/
	25.,				/* 5 ^  2			*/
	625.,				/* 5 ^  4			*/
	390625.,			/* 5 ^  8			*/
	152587890625.,			/* 5 ^ 16			*/
	23232710361480712890625.	/* 5 ^ 32			*/
};

#define	FALSE	0
#define	TRUE	1

#ifdef	decus
double
ldexp(value, exp)
union {
	double		d;
	unsigned 	i;	/* High byte has exponent		*/
} value;
int		exp;
/*
 * Hack, works only for PDP-11's
 */
{
	value.i =  (value.i & 0100177)
	       | (((value.i & 0077600) + (exp << 7)) & 077600);
	return (value.d);
}
#else
extern double	ldexp();
#endif

double
atof(text)
register char	*text;
/*
 * Return text as a floating value
 */
{
	register int		c;		/* Current character	*/
	register int		exp;		/* Exponent		*/
	int			eexp;		/* ...e exponent	*/
	int			bexp;		/* for ldexp		*/
	int			neg;		/* TRUE if negative	*/
	int			negexp;		/* TRUE if exp negative	*/
	double			v;		/* Gets value		*/
	double			flexp;		/* Gets exponent	*/

	exp = 0;
	v = 0.0;
	while ((c = *text++) == ' ')
		;				/* Skip blanks		*/
	if ((neg = (c == '-')) != FALSE)
		;
	else if (c != '+')
		text--;
	while ((c = *text++), isdigit(c)) {
		if (v < twoe56) {
			v *= 10.0;
			v += (c - '0');
		}
		else
			exp++;
	}
#ifdef TESTING
	printf("integer part seen, value %g, exp %d, stop at \"%s\"\n",
		v, exp, text - 1);
#endif
	if (c == '.') {
		while ((c = *text++), isdigit(c)) {
			if (v < twoe56) {
				v *= 10.0;
				v += (c - '0');
				exp--;
			}
		}
#ifdef TESTING
		printf("fraction, value %g, exp %d, stop at \"%s\"\n",
			v, exp, text - 1);
#endif
	}
	if (c == 'e' || c == 'E') {
		if ((negexp = ((c = *text++) == '-')) != FALSE)
			;
		else if (c != '+')
			text--;
		eexp = 0;
		while ((c = *text++), isdigit(c)) {
			eexp = (eexp * 10) + (c - '0');
		}
		if (negexp)
			eexp = -eexp;
		exp += eexp;
#ifdef TESTING
		printf("Exp seen, exp = %d, rest = \"%s\"\n",
			exp, text - 1);
#endif
	}
	if ((bexp = exp) < 0)
		exp = -exp;
	flexp = 1.0;
	for (c = 0; c < 6; c++) {
		if (exp & 01)
			flexp *= exp5[c];
		exp >>= 1;
		if (exp == 0)
			break;
	}
#ifdef TESTING
	printf("float exponent = %g, bexp = %d\n", flexp, bexp);
#endif
	if (bexp < 0)
		v /= flexp;
	else	v *= flexp;
#ifdef TESTING
	printf("value before ldexp = %g\n", v);
#endif
	v = ldexp(v, bexp);
#ifdef TESTING
	printf("value  after ldexp = %g\n", v);
#endif
	if (neg)
		return (-v);
	else	return (v);
}


#ifdef TESTING
#include <stdio.h>

char	text[80];
int	$$narg = 1;

main() {
	double v;

	while (gets(text) != NULL) {
		v = atof(text);
		printf("\"%s\" = %g", text, v);
		printf(", [%o %o %o %o]\n", v);
	}
}
#endif
