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

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

Solution by Pilcrow

I have replaced my previous effort.. This one contains a basic validity test. All input is via the keyboard...

#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;
}

Solution by blob84

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

Solution by Adam89

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


Solution by menonsahab

/* I wrote up a recursive solution. Turns out Adam beat me to it. But I've avoided using the pow() function
to keep this a category 0 solution. */

#include <stdio.h>
double atof(char *s)
{
	double res = 0.0, exponent = 0.0;
	int i = 0, c, sign = 1, flag = 0, k = 0;
	if(s[0] == '-')
		sign = -1;
	while( (c = s[i++]) != '\0')
	{
		if(c == 'e' || c =='E')
		{
			exponent = atof(&s[i]);
			break;
		}
		if(c == '.')
			flag = 1;
		else if(c >= '0' && c <= '9' )
		{
			if(flag == 1)
				++k;
			res = (c - '0') + (res * 10);
		}
	}

	exponent -= k;
	if(exponent < 0)
		while(exponent++)
			res /= 10.0;
	else if(exponent > 0)
		while(exponent--)
			res *= 10.0;

	return sign * res;
}
int main()
{
	char s1[] = "87.549e2", s2[] = "-982.47e-3", s3[] = "-54e", s4[] = "-.64e4", s5[] = "+.87e+ 6";
	printf("s1 = %10s <-> %15lf\n", s1, atof(s1));
	printf("s2 = %10s <-> %15lf\n", s2, atof(s2));
	printf("s3 = %10s <-> %15lf\n", s3, atof(s3));
	printf("s4 = %10s <-> %15lf\n", s4, atof(s4));
	printf("s5 = %10s <-> %15lf\n", s5, atof(s5));
	return 0;
}

Solution by Luke Panayi (cat 0)

My cat0 solution, using atoi instead of atof to avoid using recrusion and keep it cat0.

/*
  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>
#include <math.h>
#define BUFFER 1000

int atoi(char s[])	{
  int i, n, sign;

  for (i=0; isspace(s[i]); ++i);
  sign = (s[i] == '-') ? -1 : 1;
  if (s[i] == '+' || s[i] == '-')
    ++i;
  for (n=0; isdigit(s[i]); ++i)
    n = 10 * n + (s[i] - '0');
  return sign * n;
}

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

  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;
    char t[BUFFER];
    int j, n;
    for (j=0; s[i] != '\0'; ++j, ++i)	{
      t[j] = s[i];
    }
    t[j] = '\0';
    n = atoi(t); //wanted to use recurrsion here with atof but we haven't met that yet so keeping it category 0. Works as the expodent is always expected to be an int anyway.
    power *= pow(10,-n); //flip the sign of n as power is dividing the final value
  }

  return sign * val / power;
}

int main()	{
  char s[] = "-123.45e-6";
  printf("%f\n", atof(s));
  return 0;
}

Solution by Miguel Degrossoli

/* 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 <ctype.h>
#include <stdio.h>

#define MAXLINE 100

double atof(char s[]);
int mygetline(char s[], int maxl);

/* converts strings to double */
int main()
{
	char line[MAXLINE];

	while (mygetline(line, MAXLINE) > 0)
		printf("\t%f\n", atof(line));
	return 0;
}

/* atof: convert string s to double */
double atof(char s[])
{
	double val, power;
	int i, sign, esign, exp;

	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');
	power = 1.0;
	if (s[i] == '.')
	{
		i++;
		for (; isdigit(s[i]); i++)
		{
			val = 10.0 * val + (s[i] - '0');
			power *= 10;
		}
	}
	if (s[i] == 'e' || s[i] == 'E')
		i++;
	esign = (s[i] == '-') ? -1 : 1;
	if (s[i] == '+' || s[i] == '-')
		i++;
	for (exp = 0; isdigit(s[i]); i++)
		exp = 10 * exp + s[i] - '0';
	for (i = 0; i < exp; i++)
		power *= (esign == -1) ? 10.0 : 0.1;

	return sign * val / power;
}

/* mygetline: reads a line of up to maxl chars and stores it into s. */
int mygetline(char s[], int maxl)
{
	unsigned char c;
	int i = 0;

	while (i <= maxl && (c = getchar()) != EOF && c != '\n')
		s[i++] = c;

	s[i] = '\0';

	return i;
}

Samples:

miguel@Miguel-Notebook:~/Desenvolvimento/C$ ./exercise_4-2
123.45e-6
	0.000123
123.45e6
	123450000.000000
-123.45e-6
	-0.000123
-123.45e6
	-123450000.000000
 

Solution by anonymous

My approach is similar to most, except I created a function to handle the exponent portion. This is only because atof is a function in math.h so I couldn't use the pow function like I wanted too. My goal was to follow the coding style of the book as much as possible.

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

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

#define MAXLINE 100

double atof(char s[]);
double powd(double base, double p);

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

    return 0;
}

// convert strings to double
double atof(char s[])
{
    double val, power;
    int i, sign, signE, exp;

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

// very basic math.h version of pow. I would have just used the math.h function, but the exercise asks for the function to be called
// atof and that conflicts with an existing function in math.h. So I just wrote this instead
double powd(double base, double p)
{
    int i;
    double result;

    result = 1.0;
    if (p > 0)
        for (i = 0; i < p; ++i)
            result *= base;
    else
        for (i = p; i < 0; ++i)
            result /= base;

    return result;
}
Personal tools