From f42ae1643002dac0ededc9aae37ba28c047d89f4 Mon Sep 17 00:00:00 2001 From: zlg Date: Tue, 18 Jun 2013 04:45:27 -0500 Subject: Solve Exercise 4-3: Add modulus to RPN calculator --- ch4/4-03_calc-modulus.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 ch4/4-03_calc-modulus.c (limited to 'ch4') diff --git a/ch4/4-03_calc-modulus.c b/ch4/4-03_calc-modulus.c new file mode 100644 index 0000000..05d5c1b --- /dev/null +++ b/ch4/4-03_calc-modulus.c @@ -0,0 +1,147 @@ +#include +#include +#include + +/* The C Programming Language: 2nd Edition + * + * Exercise 4-3: Given the basic framework, it's straightforward to extend the + * calculator. Add the modulus (%) operator and provisions for negative + * numbers. + * + * Answer: Supporting modulus is indeed rather easy. The modulus operator + * divides the first operand by the second and returns the remainder, and C + * supports this out of the box, so there's no need to implement much math. + * Note that the modulus operator does integer division to get its remainder. + * Doing a "proper" modulus operator would require more work, and floats can + * be tricky. I might implement this sometime. For now, I typecasted. + * + * For negative numbers, you just need to make sure you allow the dash symbol + * in two key places. + */ + +#define MAXOP 100 +#define NUMBER '0' +#define MAXVAL 100 +#define BUFSIZE 100 + +int getop(char []); +void push(double); +double pop(void); +int getch(void); +void ungetch(int); + +int sp = 0; // Next free stack position +double val[MAXVAL]; // Value stack +char buf[BUFSIZE]; // buffer for ungetch +int bufp = 0; // next free position in buf + +/* Reverse Polish calculator: + * + * Binary operations (+-*\%) + * operand operand operator + * + * Example: 6 minus 2 in Reverse Polish Notation is "6 2 -" + */ +int main() { + int type; + double op2; + char s[MAXOP]; + + while ((type = getop(s)) != EOF) { + switch (type) { + case NUMBER: + push(atof(s)); + break; + case '+': + push(pop() + pop()); + break; + case '*': + push(pop() * pop()); + break; + case '-': + op2 = pop(); + push(pop() - op2); + break; + case '/': + op2 = pop(); + if (op2 != 0.0) { + push(pop() / op2); + } else { + printf("Error: Cannot divide by zero.\n"); + } + break; + /* Yay for modulus! */ + case '%': + op2 = pop(); + if (op2 != 0.0) { + push((int)pop() % (int)op2); + } else { + printf("Error: Cannot modulo by zero.\n"); + } + break; + case '\n': + printf("\t%.8g\n", pop()); + break; + default: + printf("Error: Unknown command %s\n", s); + break; + } + } + return 0; +} + +void push(double f) { + if (sp < MAXVAL) { + val[sp++] = f; + } else { + printf("Error: Stack full. Cannot push %g\n", f); + } +} + +double pop(void) { + if (sp > 0) { + return val[--sp]; + } else { + printf("Error: Stack empty.\n"); + return 0.0; + } +} + +int getop(char s[]) { + int i, c; + + while ((s[0] = c = getch()) == ' ' || c == '\t') { + } + s[1] = '\0'; + /* The final check is for negative numbers. */ + if (!isdigit(c) && c != '.' && c != '-') { + return c; + } + i = 0; + /* The second half of this if-statement accounts for negatives */ + if (isdigit(c) || (i == 0 && c == '-')) { + while (isdigit(s[++i] = c = getch())) { + } + } + if (c == '.') { + while (isdigit(s[++i] = c = getch())) { + } + } + s[i] = '\0'; + if (c != EOF) { + ungetch(c); + } + return NUMBER; +} + +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; + } +} -- cgit v1.2.3-70-g09d2 >-38/+46 2018-10-06cli: change "Status" heading to "Progress"zlg2-36/+40 2018-09-29Bump to 0.3alpha5 for PyPIzlg1-1/+1 2018-09-29cli: Add pretty printing to 'list' commandzlg3-17/+107 2018-09-08setup.py: Bump to alpha4 for PyPIzlg1-1/+1 2018-09-08cli: add '--raw' option to list commandzlg2-9/+45 2018-09-08Add remaining filters to vgstash packagezlg1-2/+11 2018-09-04Update LICENSE to match setup.pyzlg1-80/+67 2018-09-03Branch off from master with pytest, tox, clickzlg16-778/+779 2018-03-18Flesh out filter types and ownership statuszlg3-82/+144 2018-03-18README.mdown: break line correctlyzlg1-1/+1 2018-03-18add 'playlog' list filterzlg2-2/+9 2018-03-13Update helpers a bitzlg1-2/+9 2018-03-13Make VGSTASH_DB_LOCATION point to a filezlg2-21/+20 2016-11-18Remove settings from helpers.shZe Libertine Gamer1-5/+0 2016-11-15Correct phrasing in README.Ze Libertine Gamer1-4/+4 2016-11-13DerpZe Libertine Gamer1-0/+1 2016-11-03Improve error handling in shell scriptsZe Libertine Gamer4-3/+23 2016-10-24Correct run_again, add recursionZe Libertine Gamer1-0/+4 2016-10-21Add quotes to correct behavior for arglistZe Libertine Gamer1-1/+1 2016-10-14updater.sh: add recursion, error handlingZe Libertine Gamer1-43/+101 2016-10-14Correct pipe-handling behaviorZe Libertine Gamer1-1/+9 2016-10-12Clarify a method to move between platformsZe Libertine Gamer1-2/+5