Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 5.02 on page 97

Write getfloat , the floating-point analog of getint . What type does getfloat return as its function value?



Solution by Chris Mears

/*
 * Exercise 5-2 from The C Programming Language, 2nd edition, by Kernighan
 * and Ritchie.
 *
 * "Write getfloat, the floating-point analog of getint. What type does
 * getfloat return as its function value?"
 */

/*
 * Here's the getint function, from section 5.2:
 */

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

int getch(void);
void ungetch(int);

/* getint:  get next integer from input into *pn */
int getint(int *pn)
{
        int c, sign;
        
        while (isspace(c = getch()))   /* skip white space */
                ;
        
        if (!isdigit(c) && c != EOF && c != '+' && c != '-') {
                ungetch(c);  /* it is not a number */
                return 0;
        }
        sign = (c == '-') ? -1 : 1;
        if (c == '+' || c == '-')
                c = getch();
        for (*pn = 0; isdigit(c); c = getch())
                *pn = 10 * *pn + (c - '0');
        *pn *= sign;
        if (c != EOF)
                ungetch(c);
        return c;
}

/*
 * The getch and ungetch functions, from section 4.3, are also required.
 */

#include <stdio.h>

#define BUFSIZE 100

char buf[BUFSIZE];      /* buffer for ungetch */
int bufp = 0;           /* next free position in buf */

int getch(void)         /* get a (possibly pushed-back) character */
{
        return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)     /* push character back on input */
{
        if (bufp >= BUFSIZE)
                printf("ungetch: too many characters\n");
        else
                buf[bufp++] = c;
}

/*
 * The getfloat function.
 *
 * Reads the next number from input, and puts it into *fp.  Returns EOF for
 * end of file, zero if the next input is not a number, and a positive
 * value of the input contains a valid number.
 *
 * Based heavily on the getint function from K&R2.
 */

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

int getfloat(float *fp)
{
        int ch;
        int sign;
        int fraction;
        int digits;

        while (isspace(ch = getch()))   /* skip white space */
                ;

        if (!isdigit(ch) && ch != EOF && ch != '+'
                        && ch != '-' && ch != '.') {
                ungetch(ch);
                return 0;
        }

        sign = (ch == '-') ? -1 : 1;
        if (ch == '+' || ch == '-') {
                ch = getch();
                if (!isdigit(ch) && ch != '.') {
                        if (ch == EOF) {
                                return EOF;
                        } else {
                                ungetch(ch);
                                return 0;
                        }
                }
        }

        *fp = 0;
        fraction = 0;
        digits = 0;
        for ( ; isdigit(ch) || ch == '.' ; ch = getch()) {
                if (ch == '.') {
                        fraction = 1;
                } else {
                        if (!fraction) {
                                *fp = 10 * *fp + (ch - '0');
                        } else {
                                *fp = *fp + ((ch - '0') / pow(10, fraction));
                                fraction++;
                        }
                        digits++;
                }
        }

        *fp *= sign;

        if (ch == EOF) {
                return EOF;
        } else {
                ungetch(ch);
                return (digits) ? ch : 0;
        }
}

/*
 * Test module.
 */

#include <stdio.h>

int main(void)
{
        int ret;

        do {
                float f;

                fputs("Enter a number: ", stdout);
                fflush(stdout);
                ret = getfloat(&f);
                if (ret > 0) {
                        printf("You entered: %f\n", f);
                }
        } while (ret > 0);

        if (ret == EOF) {
                puts("Stopped by EOF.");
        } else {
                puts("Stopped by bad input.");
        }
        
        return 0;
}


Solution by Gregory Pietsch

/* Gregory Pietsch <gkp1@flash.net> Exercise 5-2 dated 2001-01-08 */

#include <ctype.h>
#include <limits.h>

/* also uses getch and ungetch from Section 4.3 */

/* number of significant digits in a double */
#define SIG_MAX 32

/* store double in d; return next character */
int getfloat(double *d)
{
    const char point = '.';     /* localeconv->decimal_point[0]; */
    int c;
    char buf[SIG_MAX], sign, sawsign, sawe, sawesign, esign;
    double x;
    static double fac[] = {0.0, 1.0e8, 1.0e16, 1.0e24, 1.0e32};
    double dpow;
    int ndigit, nsig, nzero, olead, opoint, n;
    char *pc;
    long lo[SIG_MAX / 8 + 1], lexp;
    long *pl;

    /* skip white space */
    while (isspace(c = getch()))
        ;
    if (sawsign = (c == '-' || c == '+')) {
        sign = c;
        c = getch();
    } else
        sign = '+';
    olead = -1;
    opoint = -1;
    ndigit = 0;
    nsig = 0;
    nzero = 0;
    while (c != EOF) {
        if (c == point) {
            if (0 <= opoint)
                break;  /* already seen point */
            else
                opoint = ndigit;
        } else if (c == '0') {
            /* saw a zero */
            nzero++;
            ndigit++;
        } else if (!isdigit(c))
            break;      /* found nondigit */
        else {
            /* got a nonzero digit */
            if (olead < 0)
                olead = nzero;
            else {
                /* deliver zeros */
                for ( ; 0 < nzero && nsig < SIG_MAX; --nzero)
                    buf[nsig++] = 0;
            }
            ++ndigit;
            /* deliver digit */
            if (nsig < SIG_MAX)
                buf[nsig++] = (c - '0');
        }
        c = getch();
    }
    if (ndigit == 0) {
        /* no digits? */
        *d = 0.0;
        if (c != EOF)
            ungetch(c);
        if (0 <= opoint) {
            /* saw point */
            ungetch(c = point);
        }
        if (sawsign) {
            /* saw sign */
            ungetch(c = sign);
        }
        return c;
    }
    /* skip trailing digits */
    for ( ; 0 < nsig && buf[nsig - 1] == 0; --nsig)
        ;
    /* compute significand */
    pc = buf;
    pl = &(lo[nsig >> 3]);
    for (*pl = 0, n = nsig; 0 < n; --n) {
        if ((n & 7) == 0)
            /* start new sum */
            *--pl = *pc++;
        else
            *pl = *pl * 10 + *pc++;
    }
    for (*d = (double)(lo[0]), n = 0; ++n <= (nsig >> 3); )
        if (lo[n] != 0)
           *d += fac[n] * (double)(lo[n]);
    /* fold in any explicit exponent */
    lexp = 0;
    if (c == 'e' || c == 'E') {
        /* we have an explicit exponent */
        sawe = c;
        c = getch();
        if (sawesign = (c == '+' || c == '-')) {
            esign = c;
            c = getch();
        } else
            esign = '+';
        if (!isdigit(c)) {
            /* ill-formed exponent */
            if (c != EOF)
                ungetch(c);
            if (sawesign)
                ungetch(c = esign);
            c = sawe;
        } else {
            /* get exponent */
            while (isdigit(c)) {
                /* get explicit exponent digits */
                if (lexp < 100000)
                    lexp = lexp * 10 + (c - '0');
                /* else overflow */
                c = getch();
            }
            if (esign == '-')
                lexp = -lexp;
        }
    }
    if (c != EOF)
        ungetch(c);
    if (opoint < 0)
        lexp += ndigit - nsig;
    else
        lexp += opoint - olead - nsig;
    /* this is where I pray I don't lose precision */
    esign = (lexp < 0) ? '-' : '+';
    /* if anyone has a better way of handling overflow, tell Richard Heathfield */
    if (lexp < SHRT_MIN)
        lexp = SHRT_MIN;
    if (lexp > SHRT_MAX)
        lexp = SHRT_MAX;
    if (lexp < 0)
        lexp = -lexp;
    if (lexp != 0) {
        dpow = (esign == '-') ? 0.1 : 10.0;
        while (lexp != 0) {
            /* form 10.0 to the lexp power */
            if ((lexp & 1) != 0) /* lexp is positive */
                *d *= dpow;
            lexp >>= 1;
            dpow *= dpow;
        }
    }
    /* if there was a minus sign in front, negate *d */
    if (sign == '-')
        *d = -(*d);
    return c;
}



Solution by Jesus Alvarez (Category 0)

(Solution is not robust!)

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

#define STR_MAX 1000;

int getfloat(double*);

int main()
{
        double flt = 0;
        int ret = 0;
        ret = getfloat(&flt);
        printf ("Return: %f\n", flt);
        return 0;
}

#define BUFSIZE 100

char buf[BUFSIZE];      /* Buffer for ungetch. */
int bufp = 0;           /* Next free position in buf. */

int getch(void)
{
        return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
        if (bufp >= BUFSIZE) {
                printf("Ungetch: Too many characters.\n");
        } else {
                buf[bufp++] = c;
        }
}

int getfloat(double *pn)
{
        int c, sign;
        int dec, pos = 0;
        while (isspace(c = getch()));

        if (!isdigit(c) && c != EOF && c != '+' && c != '-') {
                ungetch(c); /* it is not a number */
                return 0;
        }

        sign = (c == '-') ? -1 : 1;

        if (c == '+' || c == '-') {
                c = getch();
        }

        /* 46 is the ascii code for "." */
        for (*pn = 0; isdigit(c) || c == 46 ; c = getch()) {
                if (c != 46) {
                        pos++;
                        *pn = 10 * *pn + (c - '0');
                } else if (c == 46) {
                        dec = pos;
                }
        }

        dec = pos - dec;
        for ( ; dec != 0; dec--) {
                *pn /= 10.0;
        }

        *pn *= sign;

        if (c != EOF) {
                ungetch(c);
        }

        return c;
}


Solution by menonsahab

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

/************ Input Sanitization Routine *******************/
#define SIZE 1000
int sanitizedInput[SIZE];
int len = 0;

void getSanitizedInput(void)
{
	int i, j, c;
	while((c = getchar()) != EOF) // Read all characters from stdin and neglect anything that is not a . + - or space.
	{
		if(isdigit(c) || c == '.' || c == '+' || c == '-' || c == ' ')
			sanitizedInput[len++] = c;
	}

	for(i = 0; i < len-1; i++) // Combinations such as ++, +-, -+, --, .+, .-, .. etc are not allowed
	{
		if( (sanitizedInput[i] == '+' || sanitizedInput[i] == '-' || sanitizedInput[i] == '.') && (sanitizedInput[i+1] == ' ' || sanitizedInput[i+1] == '+' || sanitizedInput[i+1] == '-' || sanitizedInput[i+1] == '.'))
		{
			if(!((sanitizedInput[i] == '+' && sanitizedInput[i+1] == '.') || (sanitizedInput[i] == '-' && sanitizedInput[i+1] == '.'))) // But +. and -. are allowed
				sanitizedInput[i] = sanitizedInput[i+1] = -1;
		}
	}

	j = 0;
	for(i = 0; i < len; i++)
		if(sanitizedInput[i] != -1)
			sanitizedInput[j++] = sanitizedInput[i];
	len = j;
}

void viewSanitizedInput(void)
{
	int i;
	for(i = 0; i < len; i++)
		printf("%c", sanitizedInput[i]);
	printf("\n");
}

/**********************************************************/

/************************* main() begins*******************/
int main()
{
	getSanitizedInput();
	//viewSanitizedInput();

	int n = 0;
	double ar[SIZE];
	for(n = 0; n < SIZE && getfloat(&ar[n]) != EOF; n++)
		printf("ar[%d] = %lf\n", n, ar[n]);
	return 0;
}
/************************* main() ends ********************/

/****************** xgetch() function**********************/
int pos = 0;
int xgetch(void)
{
	return pos < len ? sanitizedInput[pos++] : EOF; // Read from array storing the sanitized input, if finished reading all elements then return EOF
}
/**********************************************************/

/******************** getfloat() routine ******************/
// The structure of the getfloat() function becomes simplified because all spurious input has been eliminated.
int getfloat(double *fp)
{
	int c, sign, flag = 0, k = 0;
	while(isspace(c = xgetch()));
	if(c == EOF)
		return c;
	sign = (c == '-') ? -1 : 1;
	if(c == '+' || c == '-')
		c = xgetch();
	for(*fp = 0; isdigit(c) || c == '.'; c = xgetch())
	{
		if (c == '.')
			flag = 1;
		else
		{
			*fp = 10 * *fp + c - '0';
			if(flag == 1)
				k++;
		}
	}
	*fp = sign * *fp/pow(10, k);
}
/**********************************************************/
/*
On executing the above program with the following input:

<file begins>
a    +58.479
b   *#$    -874.2154
c   	\\\	 .. 657.11258
d  ++ + - 254
e    (@)(^)	q---    -.247
f %^&#$ +.478
<file ends>

the output received is:

ar[0] = 58.479000
ar[1] = -874.215400
ar[2] = 657.112580
ar[3] = 254.000000
ar[4] = -0.247000
ar[5] = 0.478000
*/


Solution by Dawood Vora (Category 0)

(Solution is not robust!)

/*
routine getch() and ungetch() are from chap 4 of K&R bible
*/

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


int main(int argc, char **argv)
{
    double f;
    int c;
    
    while ((c = getfloat(&f)) != EOF)
        printf("%f %g %d\n",f,f,c);
	return 0;
}

/*
 * July 24 2017 
 * getfloat :   gets a float value from input stream
 */
 int getfloat(double *fp){
    
    int c;
    int start = 0;
    int sign = 1;
    double t = 0.0;
    int expsign = 1;
    int i = 0;
    
    while (isspace(c = getch()))
        ;
    if (!isdigit(c) && c != EOF && c != '+' && c != '-' && c != '.' && tolower(c) != 'e') {
        ungetch(c);
        return 0;
    }
    
    sign = (c == '-') ? -1 : 1;
    if (c == '-' || c == '+') {
        c = getch();
        if (!isdigit(c) && c != '.' && tolower(c) != 'e') {
            ungetch(c);
            return 0;
        }
    }
    
    for (*fp = 0.0; isdigit(c); c = getch())
        *fp = 10 * *fp + c - '0';
    
    if (c == '.'){
        t = 1;
        while (isdigit(c = getch())){
            *fp = 10.0 * *fp + c - '0';
            t *= 10.0;
        }
        *fp /= t;
        
    }
    if (t == 1) {  //indicate no legal char after '.'
        *fp *= sign;
        ungetch(c);
        return c;
    }
    if (*fp != 0.0)
        start = 1;   //some legal value already collected
    
    if (tolower(c) == 'e') {
        if ((c = getch()) == '-') {
            expsign = -1;
            c = getch();
        }
        for (i = 0; isdigit(c); c = getch())
            i = 10 * i + c - '0';
        if (i == 0){
            ungetch(c);
            return c;
        }
        if (start == 0)  // e is the first collected char to start computing
            *fp = 1.0;
        *fp = *fp * pow(10.0,(double)(i*expsign));
    }
    *fp *= sign;
    if (c != EOF){
        ungetch(c);
    }
    return c;
}


Solution by Luke Panayi

/*
 * As written, getint treats a + or - not followed by a digit as a
 * valid representation of zero. Fix it to push such 
 * a character back on the input.
*/

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#define BUFFER 1000

char buf[BUFFER];
int bufp=0;

void ungetch(int c)	{
	if (bufp >= BUFFER)
		printf("ungetch: too many characters\n");
	else
		buf[bufp++] = c;
}

int getch(void)	{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

char getfloat(double *pn)	{
	
	int c, sign;

	while (isspace(c = getch()));

	if (!isdigit(c) && c != EOF && c != '+' && c != '-')	{
		ungetch(c); //not a number
		return 0;
	}

	sign = (c == '-') ? -1 : 1;
	if (c == '+' || c == '-')
		c = getch();
	for (*pn = 0; isdigit(c); c = getch())
		*pn = 10 * *pn + (c - '0');
	if (c == '.')	{
		int i, dec;
		c = getch();
		for (i = 0; isdigit(c); c = getch(), i++)
			dec = 10 * dec + (c - '0');
		*pn += dec/(pow(10,i));
	}
	if (*pn == 0 && c != '0')	{ //if +/- is followed by NaN, pn will stay at 0
		ungetch(sign == 1 ? '+' : '-');
		return 0;
	}
	*pn *= sign;
	if (c != EOF)
		ungetch(c);
	return c;
}

int main()	{
	
	int n;
	double array[BUFFER];

	for (n = 0; n < BUFFER && getfloat(&array[n]) != EOF; n++);
	printf("%f\n", array[0]);

	return 0;
}

Solution by Roberto Kok (Category 0)

Not very sturdy; simply appended decimal handling to the example from the book.

/* getfloat: get next float from input into *pn */
int getfloat(double *pn)
{
    int c, i, sign;
    while (isspace(c = getch())) /* skip white space */
        ;
    if (!isdigit(c) && c != EOF && c != '+' && c != '-') {
        ungetch(c); /* it is not a number */
        return 0;
    }
    sign = (c == '-') ? -1 : 1;
    if (c == '+' || c == '-')
        c = getch(); /* skip to next char */

    for (*pn = 0; isdigit(c); c = getch())
        *pn = 10 * *pn + (c - '0');

    if (c == '.') {
        c = getch();
        for (i = 1; isdigit(c); c = getch(), i++)
            *pn += (double)(c - '0') / pow(10, i); /* add decimals */
    }

    *pn *= sign; /* convert to negative if needed */

    if (c != EOF)
        ungetch(c);
    return c;
}
Personal tools