summaryrefslogtreecommitdiff
path: root/missing_d/strtod.c
blob: 570f64070433a9e8d7f24cb8536e8b840974f723 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
 * gawk wrapper for strtod
 */
/*
 * Stupid version of System V strtod(3) library routine.
 * Does no overflow/underflow checking.
 *
 * A real number is defined to be
 *	optional leading white space
 *	optional sign
 *	string of digits with optional decimal point
 *	optional 'e' or 'E'
 *		followed by optional sign or space
 *		followed by an integer
 *
 * if ptr is not NULL a pointer to the character terminating the
 * scan is returned in *ptr.  If no number formed, *ptr is set to str
 * and 0 is returned.
 *
 * For speed, we don't do the conversion ourselves.  Instead, we find
 * the end of the number and then call atof() to do the dirty work.
 * This bought us a 10% speedup on a sample program at uunet.uu.net.
 *
 * Fall 2000: Changed to enforce C89 semantics, so that 0x... returns 0.
 * C99 has hexadecimal floating point numbers.
 *
 * Summer 2001. Try to make it smarter, so that a string like "0000"
 * doesn't look like we failed. Sigh.
 *
 * Xmass 2002. Fix a bug in ptr determination, eg. for "0e0".
 *
 * Spring 2004. Update for I18N. Oh joy.
 */

#if 0
#include <ctype.h>
#endif

extern double atof();

double
gawk_strtod(s, ptr)
const char *s;
const char **ptr;
{
	const char *start = s;		/* save original start of string */
	const char *begin = NULL;	/* where the number really begins */
	int dig = 0;
	int dig0 = 0;

	/* optional white space */
	while (isspace(*s))
		s++;

	begin = s;

	/* optional sign */
	if (*s == '+' || *s == '-')
		s++;

	/* string of digits with optional decimal point */
	while (*s == '0') {
		s++;
		dig0++;
	}
	while (isdigit(*s)) {
		s++;
		dig++;
	}

	if (
#if defined(HAVE_LOCALE_H)
	loc.decimal_point != NULL && do_posix
			? *s == loc.decimal_point[0]
			: *s == '.'
#else
	*s == '.'
#endif
	) {
		s++;
		while (*s == '0') {
			s++;
			dig0++;
		}
		while (isdigit(*s)) {
			s++;
			dig++;
		}
	}

	dig0 += dig;	/* any digit has appeared */

	/*
 	 *	optional 'e' or 'E'
	 *		if a digit (or at least zero) was seen
	 *		followed by optional sign
	 *		followed by an integer
	 */
	if (dig0
	    && (*s == 'e' || *s == 'E')
	    && (isdigit(s[1])
	      || ((s[1] == '-' || s[1] == '+') && isdigit(s[2])))) {
		s++;
		if (*s == '+' || *s == '-')
			s++;
		while (isdigit(*s))
			s++;
	}

	/* In case we haven't found a number, set ptr to start. */
	if (ptr)
		*ptr = (dig0 ? s : start);

	/* Go for it. */
	return (dig ? atof(begin) : 0.0);
}

#ifdef TEST
int
main(argc, argv)
int argc;
char **argv;
{
	double d;
	char *p;

	for (argc--, argv++; argc; argc--, argv++) {
		d = strtod (*argv, & p);
		printf ("%lf [%s]\n", d, p);
	}

	return 0;
}
#endif