aboutsummaryrefslogtreecommitdiff
path: root/ch4/4-02_atof2.c
blob: 69c42fcfba7a20187ef5c1315852cff45273c535 (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
#include <stdio.h>
#include <ctype.h>

/* The C Programming Language: 2nd Edition
 *
 * Exercise 4-2: Extend atof to handle scientific notation of the form
 *
 * 123.45e-6
 *
 * where a floating-point number may be followed by 'e' or 'E' and an
 * optionally signed exponent.
 *
 * Answer: It's relatively simple to extend atof. You need a variable to count
 * the number of places the decimal needs to be moved, and in which direction.
 * With a little clever manipulation of the `power` variable, you don't even
 * need to touch the math that's used just before atof() returns. :)
 */

double atof(char s[]) {
	double val, power;
	int i, sign, esign, exp;

	/* Skip whitespace */
	for (i = 0; isspace(s[i]); i++) {
	}

	sign = (s[i] == '-') ? -1 : 1;

	if (s[i] == '+' || s[i] == '-') {
		i++;
	}
	for (val = 0.0; isdigit(s[i]); i++) {
		val = 10.0 * val + (s[i] - '0');
	}
	if (s[i] == '.') {
		i++;
	}
	for (power = 1.0; isdigit(s[i]); i++) {
		val = 10.0 * val + (s[i] - '0');
		power *= 10.0;
	}
	/* Start the exponent checking... */
	if (s[i] == 'e' || s[i] == 'E') {
		i++;
		esign = (s[i] == '-') ? 1 : 0;
		if (s[i] == '+' || s[i] == '-') {
			i++;
		}
		/* This allows us to count how many times we need to mess with
		 * the power variable */
		exp = 0;
		while (isdigit(s[i])) {
			exp *= 10;
			exp += (s[i] - '0');
			i++;
		}
		/* And do our business, which is a bit tricky */
		while (exp > 0) {
			/* Because `power` is being divided with later, we need to reverse
			 * operands to ensure it shrinks and expands as needed. */
			if (esign == 1) {
				// A negative exponent means we need to divide by more
				power *= 10.0;
			} else {
				// And a positive exponent means we need to divide by less
				power /= 10.0;
			}
			exp--;
		}
	}
	/* The math stays the same */
	return sign * val / power;
}

int main(void) {
	// Declare the function so there's no confusion over what it returns.
	double atof(char s[]);
	/* Should print "0.0000012345678900" */
	printf("%20.16f\n", atof("   123.456789e-8     "));
	return 0;
}