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