Exercise 4-5. Add access to library functions like
sin,cos,exp, andpow. See<math.h>in Appendix B, Section 4.
Math functions need the calculator to recognize words as well as single characters. getop currently returns one character at a time. The fix: if getop sees a letter, it reads the whole word into s[] and returns a new token type MATH. The main loop then dispatches on the word using strcmp. pow is special — it consumes two operands; all others consume one. Compile with -lm to link the math library.
Key Changes
/* K&R Exercise 4-5 — RPN calculator: sin, cos, exp, pow
* Compile: gcc -ansi -Wall ex4-5.c -o ex4-5 -lm */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#define MAXOP 100
#define NUMBER '0'
#define MATH 'm'
#define MAXVAL 100
static double val[MAXVAL];
static int sp = 0;
void push(double f) { if (sp < MAXVAL) val[sp++] = f; else printf("stack full\n"); }
double pop(void) { if (sp > 0) return val[--sp]; printf("stack empty\n"); return 0.0; }
static int _buf = EOF;
int gc(void) { int c = _buf; _buf = EOF; return (c != EOF) ? c : getchar(); }
void ugc(int c) { _buf = c; }
int getop(char s[])
{
int i, c;
while ((s[0] = c = gc()) == ' ' || c == '\t')
;
s[1] = '\0';
if (isalpha(c)) { /* read a word for math functions */
for (i = 1; isalpha(s[i] = c = gc()); i++)
;
s[i] = '\0';
if (c != EOF) ugc(c);
return MATH;
}
if (!isdigit(c) && c != '.' && c != '-')
return c;
i = 0;
if (isdigit(c) || c == '-')
while (isdigit(s[++i] = c = gc()))
;
if (c == '.')
while (isdigit(s[++i] = c = gc()))
;
s[i] = '\0';
if (c != EOF) ugc(c);
return NUMBER;
}
int main(void)
{
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) push(pop()/op2); break;
case '%': op2 = pop(); if (op2) push((int)pop()%(int)op2); break;
case MATH:
if (strcmp(s, "sin") == 0) push(sin(pop()));
else if (strcmp(s, "cos") == 0) push(cos(pop()));
else if (strcmp(s, "exp") == 0) push(exp(pop()));
else if (strcmp(s, "sqrt") == 0) push(sqrt(pop()));
else if (strcmp(s, "pow") == 0) { op2 = pop(); push(pow(pop(), op2)); }
else printf("unknown function %s\n", s);
break;
case '\n': printf("\t%.8g\n", pop()); break;
}
}
return 0;
}
Test It
echo "1 sin" | ./ex4-5 # sin(1 radian) = 0.84147098
echo "2 sqrt" | ./ex4-5 # 1.4142136
echo "2 3 pow" | ./ex4-5 # 8
echo "1 exp" | ./ex4-5 # e = 2.7182818
Sample Output
0.84147098
1.4142136
8
2.7182818
What This Exercise Teaches
- Multi-character tokens — extending a character-at-a-time lexer to recognize words; the key is the
isalphaentry check ingetop - Dispatch on strings —
switchcan’t dispatch on strings;strcmpin a chain ofif/else ifis the idiomatic approach - Arity matters —
powtakes two operands (pop order matters:pop()gives the exponent, secondpop()gives the base); all other math functions take one - Linking
-lm— math functions are in a separate library on most Unix systems; GCC requires explicit-lm
Set Up Your C Environment
← Exercise 4-4 |
Chapter 4 Solutions |
Exercise 4-6 →
Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie