"The C Programming Language", 2nd edition, Kernighan and Ritchie

Answer to Exercise 4-2, page 73

Solution by Dann Corbit

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.



/*
**  Written by Dann Corbit as K&R 2, Exercise 4-2 (Page 73).
**  Keep in mind that this is *JUST* a student exercise, and is
**  light years away from being robust.
**
**  Actually, it's kind of embarassing, but I'm too lazy to fix it.
**
**  Caveat Emptor, not my fault if demons fly out of your nose,
**  and all of that.
*/
#include <ctype.h>
#include <limits.h>
#include <float.h>
#include <signal.h>
#include <stdio.h>

int my_atof(char *string, double *pnumber)
{
    /* Convert char string to double data type. */
    double          retval;
    double          one_tenth = 0.1;
    double          ten = 10.0;
    double          zero = 0.0;
    int             found_digits = 0;
    int             is_negative = 0;
    char           *num;

    /* Check pointers. */
    if (pnumber == 0) {
        return 0;
    }
    if (string == 0) {
        *pnumber = zero;
        return 0;
    }
    retval = zero;

    num = string;

    /* Advance past white space. */
    while (isspace(*num))
        num++;

    /* Check for sign. */
    if (*num == '+')
        num++;
    else if (*num == '-') {
        is_negative = 1;
        num++;
    }
    /* Calculate the integer part. */
    while (isdigit(*num)) {
        found_digits = 1;
        retval *= ten;
        retval += *num - '0';
        num++;
    }

    /* Calculate the fractional part. */
    if (*num == '.') {
        double          scale = one_tenth;
        num++;
        while (isdigit(*num)) {
            found_digits = 1;
            retval += scale * (*num - '0');
            num++;
            scale *= one_tenth;
        }
    }
    /* If this is not a number, return error condition. */
    if (!found_digits) {
        *pnumber = zero;
        return 0;
    }
    /* If all digits of integer & fractional part are 0, return 0.0 */
    if (retval == zero) {
        *pnumber = zero;
        return 1;               /* Not an error condition, and no need to
                                 * continue. */
    }
    /* Process the exponent (if any) */
    if ((*num == 'e') || (*num == 'E')) {
        int             neg_exponent = 0;
        int             get_out = 0;
        long            index;
        long            exponent = 0;
        double          getting_too_big = DBL_MAX * one_tenth;
        double          getting_too_small = DBL_MIN * ten;

        num++;
        if (*num == '+')
            num++;
        else if (*num == '-') {
            num++;
            neg_exponent = 1;
        }
        /* What if the exponent is empty?  Return the current result. */
        if (!isdigit(*num)) {
            if (is_negative)
                retval = -retval;

            *pnumber = retval;

            return (1);
        }
        /* Convert char exponent to number <= 2 billion. */
        while (isdigit(*num) && (exponent < LONG_MAX / 10)) {
            exponent *= 10;
            exponent += *num - '0';
            num++;
        }

        /* Compensate for the exponent. */
        if (neg_exponent) {
            for (index = 1; index <= exponent && !get_out; index++)
                if (retval < getting_too_small) {
                    get_out = 1;
                    retval = DBL_MIN;
                } else
                    retval *= one_tenth;
        } else
            for (index = 1; index <= exponent && !get_out; index++) {
                if (retval > getting_too_big) {
                    get_out = 1;
                    retval = DBL_MAX;
                } else
                    retval *= ten;
            }
    }
    if (is_negative)
        retval = -retval;

    *pnumber = retval;

    return (1);
}
/*
** Lame and evil wrapper function to give the exercise the requested
** interface.  Dann Corbit will plead innocent to the end.
** It's very existence means that the code is not conforming.
** Pretend you are a C library implementer, OK?  But you would fix
** all those bleeding gaps, I am sure.
*/
double atof(char *s)
{
    double          d = 0.0;
    if (!my_atof(s, &d))
    {
#ifdef DEBUG
        fputs("Error converting string in [sic] atof()", stderr);
#endif
        raise(SIGFPE);
    }
    return d;
}

#ifdef UNIT_TEST
char  *strings[] = {
    "1.0e43",
    "999.999",
    "123.456e-9",
    "-1.2e-3",
    "1.2e-3",
    "-1.2E3",
    "-1.2e03",
    "cat",
    "",
    0
};
int  main(void)
{
    int             i = 0;
    for (; *strings[i]; i++)
        printf("atof(%s) = %g\n", strings[i], atof(strings[i]));
    return 0;
}
#endif


Back to index





You are visitor number - call again soon!