diff options
author | zlg <zlg@zlg.space> | 2013-05-08 00:59:24 -0500 |
---|---|---|
committer | zlg <zlg@zlg.space> | 2013-05-08 00:59:24 -0500 |
commit | da60299c2c4ae40eadb76c4a12aa471ede286d98 (patch) | |
tree | 7aad12f207ca55f6a411d897aeb6059244258846 | |
parent | Solve Exercise 4-1: strrindex() (diff) | |
download | knr-da60299c2c4ae40eadb76c4a12aa471ede286d98.tar.gz knr-da60299c2c4ae40eadb76c4a12aa471ede286d98.tar.bz2 knr-da60299c2c4ae40eadb76c4a12aa471ede286d98.tar.xz knr-da60299c2c4ae40eadb76c4a12aa471ede286d98.zip |
Solve Exercise 4-2: atof() enhanced
-rw-r--r-- | ch4/4-02_atof2.c | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/ch4/4-02_atof2.c b/ch4/4-02_atof2.c new file mode 100644 index 0000000..69c42fc --- /dev/null +++ b/ch4/4-02_atof2.c @@ -0,0 +1,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; +} |