Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 4.02 on page 73

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.

Solution by Dann Corbit , Pilcrow and Adam89.

/*
**  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


I have replaced my previous effort.. This one contains a basic validity test. All input is via the keyboard... Pilcrow 07:54, 13 September 2011 (UTC)

#include <stdio.h>
#include <ctype.h>
#include "pilcrow.h"		/* for getline */
#define MAXLINE 1024
#define PREFIX 1
#define NUMBER 2
#define EXPONENT 3
#define EXP_DIG 4


int valid_float(char s[])
{
/* a valid float may be preceded by whitespace (\s, \t, \n, etc) */
/* leading optional + or - */
/* decimal digits and one optional . */
/* optional exponent (e/E,  optional + or -, 1 to 3 decimal digits) */
/* I could do this in one line with a regex!  */


	int i, state, point, expct, ptct;
	state = PREFIX;
	point = 0;
	expct = 0;
	ptct  = 0;


	for(i=0; s[i] != '\n' && s[i] != '\0'; i++) {
		if (isspace(s[i])) {
			if(state == PREFIX) continue;
			return 0;							/* embedded  space */
		}
		if (isdigit(s[i])) {
			if(state == PREFIX || state == NUMBER) {
				state = NUMBER;
				continue;
			}
			if(state == EXPONENT || state == EXP_DIG) {
				if(++expct > 3) {
					return 0;					/* exp may not have more than 3 digits */
				}
				state = EXP_DIG;
				continue;
			}
		}
		switch(s[i]) {
			case '.':
				if(state == PREFIX || state == NUMBER) {
					if(++ptct > 1) {
						return 0;				/* only one decimal pt */
					}
					state = NUMBER;
					continue;
				}
			case 'e': case 'E':
				if(state == NUMBER) {
					state = EXPONENT;
					continue;
				}
				return 0;						/* misplaced 'e' */
			case '-': case '+':
				if(state == PREFIX) {
					state = NUMBER;
					continue;
				}
				if(state == EXPONENT) {
					state = EXP_DIG;
					continue;
				}
				return 0;						/* misplaced sign */
			default:
				return 0;						/* illegal character */
		}
	}
	return 1;									/* everything ok */
}

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

	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;
	}
	if(s[i] == 'e' || s[i] == 'E') {
		i++;
		sign2 = (s[i] == '-') ? -1 : 1;
		if(s[i] == '+' || s[i] == '-')
			i++;
		for(exponent = 0; isdigit(s[i]); i++)
			exponent = 10 * exponent + (s[i] - '0');
		if(sign2 ==  1) for(i = exponent; i > 0; --i, val *=10.0);
		if(sign2 == -1) for(i = exponent; i > 0; --i, val /=10.0);
	}
	return sign * val / power;
}

int main(void)
{
	char inp[MAXLINE+1];

	while(getline(inp, MAXLINE-1) > 1)	{	/* terminates on empty line */
		if(!valid_float(inp)) {
			printf("invalid input\n");
		}else
		printf("% 9.9g\n", atof(inp));
	}
	return 0;
}


blob84 14:39, 19 november 2012 (UTC)

/* 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.
*/

#include <stdio.h>
#include <ctype.h>

double atof(char s[]);

int main(void)
{
	printf("%f\n", atof("123.45e-6"));

}

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

	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;		
	}
	if (s[i] == 'e' || s[i] == 'E')
		i++;
	else
		return sign * val/power;
	base = (s[i] == '-') ? 0.1 : 10.0; /* 10^(-n) = 1/10^n = (1/10)^n = (0.1)^n */
	if (s[i] == '+' || s[i] == '-')
		i++;	
	for (exp = 0; isdigit(s[i]); i++) 
		exp = 10 * exp + (s[i] - '0');
	for (p = 1; exp > 0; --exp)
		p = p * base;

	return (sign * (val/power)) * p;
}


Adam89 (talk) 22:00, 21 May 2013 (UTC): My solution is a small addition to the original "atof" function from K&R page 71. It is recursive and will work with positive or negative exponents, and will work if there is whitespace between the base number, the letter 'e' and the exponent. No validity checks, so it does assume the input is of a valid format e.g. only 1 decimal point in the exponent.

/*************************************
myatof: return double from string input.
Can also account for scientific input
including negative and/or non-integer 
exponent, and white space between
base, e, and exponent.

e.g. "12.345 e -5.337" = 0.00005681867
*************************************/


#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<math.h>

double myatof(char s[]);

int main(void) {

	char num[] = "1288644.345 e -5.337";

	printf("%.12f\n",myatof(num));

	return 0;
}

double myatof(char s[]) {
	double val, power, exponent;
	int i, sign;
	for (i = 0; isspace(s[i]); i++) /*skip white space*/
		;
		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;
	}
	for ( ; isspace(s[i]); i++) /*skip any further white space up to an e or E*/
		;
        
	if ((s[i] != '\0') && (tolower(s[i++]) == 'e')) /*** See 1 below ***/
                exponent = myatof(&s[i]);               /*** See 2 below ***/
	else 
		exponent = 0.0;

	return (sign * val / power) * pow(10.0,exponent);

        /* 1: Just in case we arrive at '\0' and , by some coincidence, the next char in memory 
        happens to be 'e', we should stop or there would be problems i.e. we should only get 
        the exponent if we are still inside s.*/

        /* 2: Note the recursion here; for an original argument of "52.553 e -8.35", this line
        will pass the string " -8.35" and return -8.35 as double.  If the exponent is omitted
        and a string of whitespace/an empty string is passed here, then 0.0 is returned.*/
}

Personal tools
Personal tools
Tidy_icons
not logged in