aboutsummaryrefslogtreecommitdiff
path: root/ch4/4-02_atof2.c
diff options
context:
space:
mode:
authorzlg <zlg@zlg.space>2013-05-08 00:59:24 -0500
committerzlg <zlg@zlg.space>2013-05-08 00:59:24 -0500
commitda60299c2c4ae40eadb76c4a12aa471ede286d98 (patch)
tree7aad12f207ca55f6a411d897aeb6059244258846 /ch4/4-02_atof2.c
parentSolve Exercise 4-1: strrindex() (diff)
downloadknr-da60299c2c4ae40eadb76c4a12aa471ede286d98.tar.gz
knr-da60299c2c4ae40eadb76c4a12aa471ede286d98.tar.bz2
knr-da60299c2c4ae40eadb76c4a12aa471ede286d98.tar.xz
knr-da60299c2c4ae40eadb76c4a12aa471ede286d98.zip
Solve Exercise 4-2: atof() enhanced
Diffstat (limited to '')
-rw-r--r--ch4/4-02_atof2.c81
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;
+}