I'm doing a homework assignment in C. I have to build a calculator that takes in RPN, converts it to a double, adds / removes it from stack and prints what's left on the stack.
Everything seems to be going well until I run it. Upon entering my input, the char[] gets successfully converted to double (or so i think) and somewhere along the way (maybe in getop()) does the program think that i'm not entering digits. Here is the code and the output.
#include <stdio.h>
#define MAXOP 100 /* maximum size of the operand of operator */
#define NUMBER '0' /* signal that a number was found */
int getOp(char []); /* takes a character string as an input */
void push(double);
double pop(void);
double asciiToFloat(char []);
/* reverse polish calculator */
int main()
{
int type;
double op2;
char s[MAXOP];
printf("Please enter Polish Notation or ^d to quit.\n");
while ((type = getOp(s)) != EOF) {
switch (type) {
case NUMBER:
push(asciiToFloat(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 : zero divisor!\n");
break;
case '\n':
printf("\t%.2f\n", pop()); /* this will print the last item on the stack */
printf("Please enter Polish Notation or ^d to quit.\n"); /* re-prompt the user for further calculation */
break;
default:
printf("error: unknown command %s.\n", s);
break;
}
}
return 0;
}
#include <ctype.h>
/* asciiToFloat: this will take ASCII input and convert it to a double */
double asciiToFloat(char s[])
{
double val;
double power;
int i;
int sign;
for (i = 0; isspace(s[i]); i++) /* gets rid of any whitespace */
;
sign = (s[i] == '-') ? -1 : 1; /* sets the sign of the input */
if (s[i] == '+' || s[i] == '-') /* excludes operands, plus and minus */
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;
}
return sign * val / power;
}
#define MAXVAL 100 /* maximum depth of value stack */
int sp = 0; /* next free stack position */
double val[MAXVAL]; /* value stack */
/* push: push f onto value stack */
void push(double f)
{
if (sp < MAXVAL) {
val[sp++] = f; /* take the input from the user and add it to the stack */
printf("The value of the stack position is %d\n", sp);
}
else
printf("error: stack full, cant push %g\n", f);
}
/* pop: pop and return the top value from the stack */
double pop(void)
{
if (sp > 0)
return val[--sp];
else {
printf("error: stack empty\n");
return 0.0;
}
}
#include <ctype.h>
int getch(void);
void ungetch(int);
/* getOp: get next operator or numeric operand */
int getOp(char s[])
{
int i;
int c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.') {
printf("Neither a digit or a decimal.\n");
return c; /* neither a digit nor a decimal */
}
i = 0;
if (isdigit(c)) /* grab the integer */
while (isdigit(s[++i] = c = getch()))
;
if (c == '.') /* grab the fraction */
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
#define BUFSIZE 100
char buf[BUFSIZE]; /* buffer for ungetch */
int bufp = 0; /* next free position in buffer */
/* getch: get a number that may or may not have been pushed back */
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
/* ungetch: if we read past the number, we can push it back onto input buffer */
void ungetch(int c)
{
if (bufp >= BUFSIZE)
printf("ungetch: to many characters.\n");
else
buf[bufp++] = c;
}
Output :
Please enter Polish Notation or ^d to quit.
123
The value of the stack position is 1
Neither a digit or a decimal.
123.00
Please enter Polish Notation or ^d to quit.
Any thoughts regarding what's going on will be super helpful. It seems like then number is properly getting passed in, properly formatted from char to double, and then something goes wrong.
Thank you.
Rock
Change
printf("Neither a digit or a decimal.\n");
to
printf("Neither a digit or a decimal: %d 0x%x.\n", c, c);
so you can see what is causing the message.
Your output shows that getch() is returning the line feed ("\n", 0x0A) at the end of the line. Also, unless you were required to write asciiToFloat() for your homework assignment, you should use atof() or strtod() (both declared in "stdlib.h") from the Standard C Library. They are (usually?) implemented to avoid loss of precision and accuracy during the conversion; repeatedly multiplying by 10 causes a loss of same. Nice looking code, otherwise! :)
Related
I have written my code to evaluate from postfix to result. However, I am stuck at how to do it when the postfix is going to be in decimals & floating point numbers in scientific e notation - e.g. {1.23e4}. Any specific suggestion would be highly appreciated. Thanks.
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#define SIZE 50 /* Size of Stack */
double s[SIZE];
int top=-1; /* Global declarations */
int flag=0;
double pop()
{ /* Function for POP operation */
return(s[top--]);
}
double push(double elem)
{ /* Function for PUSH operation */
if(flag==1){
int num;
num=pop();
s[++top]=elem+10*num;
}
else if(flag==0){
s[++top]=elem;
flag=1;
}
}
void main()
{ /* Main Program */
char pofx[50],ch;
int i=0;
double op1,op2;
printf("Enter the Postfix Expression:");
fgets(pofx,100,stdin);
while( (ch=pofx[i++]) != '\n')
{
if(isdigit(ch)) push(ch-'0'); /* Push the operand */
else if(ch==' ')
flag=0;
else
{ /* Operator,pop two operands */
flag=0;
op2=pop();
op1=pop();
switch(ch)
{
case '+':push(op1+op2);break;
case '-':push(op1-op2);break;
case '*':push(op1*op2);break;
case '/':push(op1/op2);break;
case '^':push(pow(op1,op2));break;
default:
printf("Input invalid ... give proper input\n");
return 0;
}
}
}
printf("Result: %lf\n",s[top]);
}
How do I evaluate decimals & floating point numbers in scientific e notation ... (?)
To convert a string into FP value use strtod() #M Oehm
Yet code has other problems in that the operator symbols '-' and '+' may also begin valid value tokens like -123.45.
// insufficient test to determine if the next part of the string is a number or operator.
if(isdigit(ch))
push(ch-'0');
Use strtod() to convert text to double and determine if the next part of the string is a double.
Alternative code:
const char *st = pofx;
while (*st) {
char *end; //location to store end of FP parsing
double value = strtod(st, &end);
if (end > st) {
push(value);
st = end;
} else if (isspace((unsigned char) *st)) {
st++;
} else {
switch (*st) {
case '+':push(pop() + pop());break; // pop order irrelevant
case '-':{ double t = pop(); push(pop() - t);break; } // pop order relevant
case '*':push(pop() * pop());break;
...
default: {
printf("Input invalid operator: character code %d\n", *st);
return 0;
}
} // end switch
st++;
}
}
Re-write push()
void push(double elem) {
if (top + 1 >= SIZE) {
printf("Stack overflow\n");
return;
}
s[++top] = elem;
}
Wrong argument to fgets()
char pofx[50];
// fgets(pofx,100,stdin); // 100??
fgets(pofx, sizeof pofx, stdin); // better
The function strtod from <stdlib.h> will parse a double for you. It takes the string to parse and a pointer to a string, that will begin with the first unparsed character. (There's a similar function for long integers, strtol.)
Here's an example of how this might work in your case. (The code just prints out the tokens and doesn't do any calculations.)
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
int is_number(const char *p)
{
if (*p == '-' || *p == '+') p++;
if (*p == '.') p++;
return isdigit((unsigned char) *p);
}
int main(void)
{
const char *line = " .1 20.1* 1.0e-3+2e+7 * -1.0*";
const char *p = line;
while (isspace((unsigned char) *p)) p++;
while (*p) {
if (is_number(p, after_op)) {
char *end;
double x = strtod(p, &end);
if (p == end) {
puts("Illegal number");
p++;
} else {
printf("Number %g\n", x);
p = end;
}
} else {
int c = *p++;
switch (c) {
case '+': puts("Operator add"); break;
case '-': puts("Operator sub"); break;
case '*': puts("Operator mult"); break;
case '/': puts("Operator div"); break;
case '^': puts("Operator pow"); break;
default: printf("Illegal char '%c'\n", c);
}
}
while (isspace((unsigned char) *p)) p++;
}
return 0;
}
This is still very crude, mind you. Strings like 20x21 will be parsed as number 20, unknown character x and number 21. While this code is bad at detecting and reporting errors (which really should be done, but it's left as an exercise, yadda, yadda), it works for valid input.
[Edit: I've incorporated chux's suggestions and also made the code compatible to read numbers with explicit signs, but that means that the binary operators - and + cannot immediately be followed by a digit. Pick your poison. I've also allowed the abridged version that leaves out a leading zero, e.g. .12, a format that I'm not very fond of.]
So I am writing a program that takes input char by char and converts each char into a binary value. It then converts the binary to random letters, each letters ascii code value is even or odd, even representing 0's and odds representing 1's. My program fully works as intended EXCEPT with binary numbers that should have a 0 as the 1st digit.
For example.
"H" = "IVLSXDX" which converts to 1001000, the decimal value of "H".
Now the problem is with characters that have a preceding 0, such as:
"#" should convert to 100011, but in the end converts to 1000110, which is throwing off the value and turns "#" into "F".
Here is my code:
#include <stdio.h>
#include <stdlib.h>
/*Returns an even valued ascii uppercase letter. Random */
char genEven() {
int value = (rand() % 13);
char evens[13] =
{'B','D','F','H', 'J','L','N','P','R','T','V','X','Z'};
return evens[value];
}
/** Returns an odd ascii uppercase letter. Random */
char genOdd() {
int value = (rand() % 13);
char odds[13] =
{'A', 'C', 'E', 'G', 'I', 'K', 'M', 'O', 'Q', 'S', 'U', 'W', 'Y'};
return odds[value];
}
/* Given a decimal, returns 7 bit binary representation. */
int dToB(int n) {
int remainder;
int binary = 0;
int i = 1;
while(n != 0) {
remainder = n%2;
n = n/2;
binary= binary + (remainder*i);
i = i*10;
}
return binary;
}
/*Reverses binary number for string conversion. */
int revBin(int n) {
int remainder;
int reversedNumber = 0;
while(n != 0)
{
remainder = n%10;
reversedNumber = reversedNumber*10 + remainder;
n /= 10;
}
return reversedNumber;
}
/* Takes binary and converts to randomized letter combos. */
void bToS(int n) {
int i = 7;
int r = n % 10;
char tmp;
//FIX SO THAT IF A BINARY HAS A 0 in the highest order spot, print an even
while(i > 0) {
if(r == 0) {
tmp = genEven();
printf("(%c-%d)", tmp, tmp);
} else {
tmp = genOdd();
printf("(%c-%d)",tmp,tmp);
}
n = n/10;
r = n%10;
i--;
}
}
/* Discards to EOL. */
void discardToEOL() {
char ch;
do {
scanf("%c", &ch);
} while(ch != '\n');
}
/* Encodes text.*/
int encode(void) {
char ch;
printf("? ");
discardToEOL(); /*Discards any newlines from previous. */
/*Get Input */
do {
scanf("%c", &ch);
if (ch >= 0 && ch <= 128 && ch != 10) {
printf("\nBinary rep of %c= %d\n", ch, dToB(ch));
bToS(revBin(dToB(ch))); //Decimal to bin-> binary to string
}
} while(ch != '\n');
printf("\n");
}
/** given a binary number, convert to decimal. */
int binToDec(int binary)
{
int decimal = 0, i = 1, remainder;
/* Iterate until number becomes zero */
while (binary != 0)
{
remainder = binary % 10;
decimal = decimal + remainder * i;
i = i * 2;
binary = binary / 10;
}
printf("%c", decimal);
}
/* Decodes text.*/
int decode(void) {
char ch;
printf("? ");
int i = 0;
int bin = 0;
discardToEOL(); /*Discards any newlines from previous. */
/*Get Input */
do {
scanf("%c", &ch);
if (ch >= 0 && ch <= 128 && ch != 10) {
if(i <= 7) {
if(ch % 2 == 0) {
//Add 0 to end
bin = bin * 10; //FIX
} else {
//Add 1 to end
bin = (bin*10) +1; //FIX
}
i++;
}
if(i == 7) {
//Send in for decimal conversion
binToDec(bin);
//Reset binary number
bin = 0;
i = 0;
}
}
} while(ch != '\n');
printf("\n");
}
/* Main */
int main(void) {
int c = 1;
printf("C/c : Encode\nD/d : Decode\nQ/q : Quit\n");
while(c) {
printf("> ");
char choice;
scanf(" %c", &choice);
switch(choice) {
case 'c':
encode();
break;
case 'C':
encode();
break;
case 'd':
decode();
break;
case 'D':
decode();
break;
case 'q':
c = 0;
break;
case 'Q':
c = 0;
break;
default:
printf("Invalid Input.\n");
break;
}
}
}
The problem you are having with padding your conversion is due to logic error. To provide padding for the conversion, you simply want to output 8-bits all the bits in your char. To output only 7-bits you output an unpadded representation of each character). It is up to you to account for each bit and each character you want to output.
By way of example, to output only a 7-bit representation of any character (0 <= c < 127), you can output an unpadded representation with the following:
/** unpadded binary representation of 'v'. */
void binprn (const uint64_t v)
{
if (!v) { putchar ('0'); return; };
size_t sz = sizeof v * CHAR_BIT;
uint64_t rem = 0;
while (sz--)
if ((rem = v >> sz))
putchar ((rem & 1) ? '1' : '0');
}
If you want to pad to a specific number of bits, you must take that number of bits (whether 0 or 1), e.g.
/** binary representation of 'v' padded to 'sz' bits.
* the padding amount is limited to the number of
* bits in 'v'. valid range: 0 - sizeof v * CHAR_BIT.
*/
void binpad (const uint64_t v, size_t sz)
{
if (!sz) { fprintf (stderr, "error: invalid sz.\n"); return; }
if (!v) { while (sz--) putchar ('0'); return; }
if (sz > sizeof v * CHAR_BIT)
sz = sizeof v * CHAR_BIT;
while (sz--)
putchar ((v >> sz & 1) ? '1' : '0');
}
(where CHAR_BIT is defined in <limits.h> (generally 8-bits per-char))
A short example would be:
#include <stdio.h>
#include <stdint.h> /* for exact width types */
#include <limits.h> /* for CHAR_BIT */
/** unpadded binary representation of 'v'. */
void binprn (const uint64_t v)
{
if (!v) { putchar ('0'); return; };
size_t sz = sizeof v * CHAR_BIT;
uint64_t rem = 0;
while (sz--)
if ((rem = v >> sz))
putchar ((rem & 1) ? '1' : '0');
}
/** binary representation of 'v' padded to 'sz' bits.
* the padding amount is limited to the number of
* bits in 'v'. valid range: 0 - sizeof v * CHAR_BIT.
*/
void binpad (const uint64_t v, size_t sz)
{
if (!sz) { fprintf (stderr, "error: invalid sz.\n"); return; }
if (!v) { while (sz--) putchar ('0'); return; }
if (sz > sizeof v * CHAR_BIT)
sz = sizeof v * CHAR_BIT;
while (sz--)
putchar ((v >> sz & 1) ? '1' : '0');
}
int main (void) {
/* output binary `a` - `z` unpadded */
for (int i = 'a'; i <= 'z'; i++) {
printf (" %c - ", i);
binprn (i);
putchar ('\n');
}
putchar ('\n');
/* output binary `a` - `z` padded to CHAR_BIT bits */
for (int i = 'a'; i <= 'z'; i++) {
printf (" %c - ", i);
binpad (i, CHAR_BIT);
putchar ('\n');
}
return 0;
}
Example Use/Output
$ ./bin/binchar
a - 1100001
b - 1100010
c - 1100011
d - 1100100
<snip>
x - 1111000
y - 1111001
z - 1111010
a - 01100001
b - 01100010
c - 01100011
d - 01100100
<snip>
x - 01111000
y - 01111001
z - 01111010
Now Turning to Your Actual Problems
You have other significant "irregularities" in your code that may cause you headaches. Parts of your code are doing what you want them to do, others are full of traps for the unwary. Let's start with discardToEOL. ch must be type int to detect EOF. Also, do not use scanf to get a single character (or int in this case), use getchar, e.g.
/* Discards to EOL. */
void discardToEOL ()
{
int c;
for (c = getchar (); c != '\n' && c != EOF; c = getchar()) {}
}
You have a similar problem with ch in decode, it should be an int which would allow simplified input with getchar. Additionally, you are not converting the ASCII character you read to a decimal value. Your logic for adding 0 or 1 to the end of bin is also flawed. You can rework it similar to:
/* Decodes binary text.*/
void decode (void)
{
int ch, i = 0, bin = 0;
discardToEOL (); /* discards all chars in input buffer (stdin) */
printf ("? ");
for (;;) {
/* loop reading ch until valid ch received, checking for EOF */
while ((ch = getchar()) != '\n' && ch != EOF) {
if (ch == '0' || ch == '1')
break;
fprintf (stderr, "error: invalid input.\n? ");
}
if (ch == EOF) { /* alway check EOF */
fprintf (stderr, "error: user canceled input.\n");
exit (EXIT_SUCCESS);
}
if (ch == '\n') /* if ch = '\n', your done */
break;
ch -= '0'; /* convert ASCII character to decimal value */
if (i <= 7) {
/* short form of what is commented below */
bin = (ch & 1) ? (bin << 1) | 1 : bin << 1;
// if (!(ch & 1))
// bin <<= 1; /* add 0 to end */
// else
// bin = (bin << 1) | 1; /* add 1 to end */
i++;
}
if (i == 7) {
binToDec (bin); /* send in for decimal conversion */
bin = i = 0; /* reset bin and i */
}
}
printf ("\n");
}
Next, the binToDec conversion is wrong. ("What did the duck say about those lines?"). Not that any conversion is required as you are passing an int anyway, but for the learning value of using the bits to arrive at an integer value, you multiply and shift, not modulo and divide, e.g. something similar to:
/** given a binary number, convert to decimal. */
void binToDec (int binary)
{
int decimal = 0, i = 1;
/* Iterate until number becomes zero */
while (binary != 0) {
decimal += (binary & 1) * i; /* your conversion was wrong */
i = i * 2;
binary >>= 1;
}
printf ("%c [%d]", decimal, decimal);
}
Your encode largely works, although your lack of input validation will kill you. You can do something like the following to improve your chances:
/* Encodes text.*/
void encode (void)
{
char ch;
discardToEOL (); /*Discards any newlines from previous. */
printf ("? ");
do {
int retval; /* always ALWAYS check the return of scanf */
while ((retval = scanf ("%c", &ch)) != 1) { /*Get Input */
if (retval == EOF) { /* alway check EOF */
fprintf (stderr, "error: user canceled input.\n");
exit (EXIT_SUCCESS);
}
fprintf (stderr, "error: invalid input.\n? ");
}
if (ch >= 0 && ch != '\n') {
/* tack your zero on here :) */
printf ("\nBinary rep of %c= 0%d\n", ch, dToB (ch));
bToS (revBin (dToB (ch))); //Decimal to bin-> binary to string
}
} while (ch != '\n');
printf ("\n");
}
(note: you can change scanf for getchar here as well (and again in main), but that is an exercise left to you)
The remaining functions are relatively unchanged. I would make a few tweaks to genEven/Odd to tidy them up as follows, e.g.
/*Returns an even valued ascii uppercase letter. Random */
char genEven ()
{
char evens[] =
{ 'B', 'D', 'F', 'H', 'J', 'L', 'N', 'P', 'R', 'T', 'V', 'X', 'Z' };
return evens[rand() % (sizeof evens / sizeof *evens)];
}
Your main needs work. From a basic standpoint start with main being a function of type int and it returns a value. See See What should main() return in C and C++?. You have the same lack of validation that will kill you. You can also tidy things up by converting the input character to lower-case (either using the tolower function in <ctype.h>, or, since this exercise is about understanding bits, by simply making sure bit-5 (the 6th bit) is 1, and if not setting it to 1. Putting those pieces together, you could do something like:
/* Main */
int main (void)
{
int c = 1;
printf ("C/c : Encode\nD/d : Decode\nQ/q : Quit\n");
while (c) {
char choice;
int retval;
printf ("> ");
for (;;) { /* loop until input conditions satisified */
while ((retval = scanf (" %c", &choice)) != 1) {
if (retval == EOF) { /* check for manual EOF */
fprintf (stderr, "error: user canceled input.\n");
return 0;
}
discardToEOL (); /* protect against multi char input */
fprintf (stderr, "error: invalid input.\n> ");
}
if (((choice >> 5) & 1) == 0) /* if lower-case bit not set */
choice |= (1 << 5); /* set lower-case bit */
/* validate choice is 'c', 'd' or 'q' */
if (choice == 'c' || choice == 'd' || choice == 'q')
break;
discardToEOL (); /* protect against multi char input */
fprintf (stderr, "error: invalid choice.\n> ");
}
switch (choice) { /* handle choice */
case 'c':
encode ();
break;
case 'd':
decode ();
break;
case 'q':
c = 0;
break;
default:
printf ("Invalid Input.\n");
break;
}
}
return 0;
}
If you tidy all things up, you can expect the following example input/output:
Example Use/Input
$ ./bin/cbd
C/c : Encode
D/d : Decode
Q/q : Quit
> c
? abcd
Binary rep of a= 01100001
(A-65)(S-83)(X-88)(J-74)(D-68)(D-68)(Y-89)(H-72)
Binary rep of b= 01100010
(C-67)(O-79)(F-70)(H-72)(B-66)(I-73)(Z-90)(D-68)
Binary rep of c= 01100011
(S-83)(U-85)(V-86)(X-88)(P-80)(Q-81)(G-71)(H-72)
Binary rep of d= 01100100
(G-71)(K-75)(F-70)(H-72)(U-85)(J-74)(T-84)(Z-90)
> d
? 1100001
a [97]
> d
? 1100011
c [99]
> foo
error: invalid choice.
> Q
There are many ways to do this and I have left you a bit to do. Go though things line-by-line, character-by-character of the code while talking to the duck. If you are still stumped, let me know and I'm happy to help you further.
Hi there I was hoping to find help here. I don't understand the following behavior. Please note that this is an exercise from The C Programming Language (4-4).
It is about the double top(void) definition in the stack.c. It should return the top of the stack as a double.
When I try to print it (with the command p) then the number format is not correct.
If I put the printf(...) in the top function itself then the format is correct.
I don't understand why that is.
I am using NetBeans 8.0.2 with default gcc compiler.
main.c
#include <stdio.h>
#include <stdlib.h>
#define MAXOP 100 /* max size of operand or operator */
#define NUMBER '0' /* signal that a number was found */
int getop(char []);
void push(double);
double pop(void);
/* reverse Polish calculator */
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: zero divisor\n");
break;
case '%':
op2 = pop();
if (op2 != 0.0)
push((int) pop() % (int) op2);
else
printf("error: zero divisor\n");
break;
case 'p':
printf("\t%.8g\n", top());
break;
case 'd':
push(top());
break;
case 's':
printStack();
break;
case '\n':
printf("\t%.8g\n", pop());
break;
default:
printf("error: unknown command %s\n", s);
break;
}
}
return 0;
}
stack.c
#include <stdio.h>
#define MAXVAL 100 /* maximum depth of val stack */
int sp = 0;
/* next free stack position */
double val[MAXVAL];
/* value stack */
/* push: push f onto value stack */
void push(double f) {
if (sp < MAXVAL)
val[sp++] = f;
else
printf("error: stack full, can′t push %g\n", f);
}
/* pop: pop and return top value from stack */
double pop(void) {
if (sp > 0)
return val[--sp];
else {
printf("error: stack empty\n");
return 0.0;
}
}
/* top: return top value from stack */
double top(void) {
if (sp > 0) {
return val[sp - 1];
} else {
printf("error: stack empty\n");
return 0.0;
}
}
/* swap two top values */
void swap(void) {
if (sp > 1) {
double t1 = pop();
double t2 = pop();
push(t1);
push(t2);
} else
printf("error: stack (almost) empty\n");
}
/* clear the stack */
void clear(void) {
sp = 0;
}
/* print the stack */
void printStack(void) {
int i;
for (i = sp - 1; i >= 0; i--)
printf("%g ", val[i]);
}
getop.c
#include <ctype.h>
#include <stdio.h>
#define NUMBER '0' /* signal that a number was found */
int getch(void);
void ungetch(int);
/* getop: get next operator or numeric operand */
int getop(char s[]) {
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.' && c != '-')
return c;
if (c == '-') { // possible negative number
if (!isdigit(s[0] = c = getch())) { // minus operator
ungetch(c);
s[0] = c = '-';
return c;
} else { // negative number
s[0] = '-';
s[1] = c;
i = 1;
}
} else
i = 0; // positive number
if (isdigit(c)) /* collect integer part */
while (isdigit(s[++i] = c = getch()))
;
if (c == '.') /* collect fraction part */
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
getch.c
#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;
}
Your main.c does not know how top is defined. It decides on a default return value of int, which leads to undefined behaviour when printing it with a format designed for double. If you enable warnings, which you should, you'll get something like "Implicit declaration of top".
You have written the declarations of the stack functions push and pop at the top of main.c, but not the declaration of top.
Including the required prototypes by hand is not good practice. It is better to declare the interface of a compilation unit in a header file:
// stack.h
#ifndef STACK_H_INCLUDED
#define STACK_H_INCLUDED
void push(double f);
double pop(void);
double top(void);
void swap(void);
#endif
Include that header file from the code where you wish to use the interface. You will always have the correct and up-to-date version.
Include the header from stack.c, too, to ensure that the published interface and the actual implementation are consistent.
%g prints shorter of the two representations: f or e .
example:
1) printf( "%g", 3.1415926 );
OUTPUT: 3.1515926
2) printf( "%g", 93000000.0 );
OUTPUT: 9.3e+07
Where scientific notation is most appropriate.
I'm not sure where to post this but I think I found a pretty major bug in K&R's polish calculator program. Basically, when you perform an operation, two values get popped while only the result gets pushed. The problem is that the result isn't getting pushed to the top of the stack! Here's an illustration:
The full code for the polish calculator provided by the textbook is shown below:
#include <stdio.h>
#include <stdlib.h> /* for atof() */
#define MAXOP 100 /* max size of operand or operator */
#define NUMBER '0' /* signal that a number was found */
int getop(char []);
void push(double);
double pop(void);
/* reverse Polish calculator */
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: zero divisor\n");
break;
case '\n':
printf("\t%.8g\n", pop());
break;
default:
printf("error: unknown command %s\n", s);
break;
}
}
system("Pause");
return 0;
}
#define MAXVAL 100 /* maximum depth of val stack */
int sp = 0; /* next free stack position */
double val[MAXVAL]; /* value stack */
/* push: push f onto value stack */
void push(double f)
{
if ( sp < MAXVAL)
val[sp++] = f;
else
printf("error: stack full. can't push %g\n", f);
}
/* pop: pop and return top value from stack */
double pop(void)
{
if (sp > 0)
return val[--sp];
else {
printf("error: stack empty\n");
return 0.0;
}
}
#include <ctype.h>
int getch(void);
void ungetch(int);
/* getop: get next operator or numeric operand */
int getop(char s[])
{
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.')
return c; /* not a number */
i = 0;
if (isdigit(c)) /*collect integer part*/
while (isdigit(s[++i] = c = getch()))
;
if (c == '.') /*collect fraction part*/
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
#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;
}
If you want to check for yourself, all I did was add
static int pass = 0;
int i, check;
i = check = 0;
inside the while loop in main() and
if(!check) {
printf("pass #%d\n",pass++);
while(val[i] != '\0') {
printf("val[%d]: %.2f\n",i,val[i]);
++i;
}
}
at the end of the while loop, just after the switch statement. I also put check = 1; in the case for '\n'.
As a possible workaround I re-wrote the pop function such that popped values are removed from the val array as soon as they are accessed. So instead of
double pop(void)
{
if (sp > 0)
return val[--sp];
else {
printf("error: stack empty\n");
return 0.0;
}
}
you'd have something like
double pop(void)
{
if (sp > 0) {
double temp = val[--sp];
val[sp] = '\0';
return temp;
}
else {
printf("error: stack empty\n");
return 0.0;
}
}
I also re-wrote the push function to ensure that values are always pushed to the end of the val array. So instead of
void push(double f)
{
if ( sp < MAXVAL)
val[sp++] = f;
else
printf("error: stack full. can't push %g\n", f);
}
you'd have
void push(double f)
{
if ( sp < MAXVAL) {
while (val[sp] != '\0')
++sp;
val[sp++] = f;
}
else
printf("error: stack full. can't push %g\n", f);
}
Even with these changes, I still had to re-write
case '\n':
printf("\t%.8g\n", pop());
break;
to retrieve the value at the top of the stack without popping it, which required replacing the printf statement with a simple function like
void print_top(void)
{
int i = 0;
while( val[i] != '\0' )
++i;
--i;
printf("\t%.8g\n",val[i]);
}
Only then does the polish calculator seem to function as intended, at least in terms of what the stack is doing behind the scenes. You can try it out for yourself with the modified code:
#include <stdio.h>
#include <stdlib.h> /* for atof() */
#include <ctype.h>
#define MAXOP 100 /* max size of operand or operator */
#define NUMBER '0' /* signal that a number was found */
#define MAXVAL 100 /* maximum depth of val stack */
int getop(char []);
void push(double);
double pop(void);
void print_top(void);
int sp = 0; /* next free stack position */
double val[MAXVAL]; /* value stack */
/* reverse Polish calculator */
main()
{
int type;
double op2;
char s[MAXOP];
while ((type= getop(s)) != EOF) {
static int pass = 0;
int i, check;
i = check = 0;
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: zero divisor\n");
break;
case '\n':
print_top();
check = 1;
break;
default:
printf("error: unknown command %s\n", s);
break;
}
if(!check) {
printf("pass #%d\n",pass++);
while(val[i] != '\0') {
printf("val[%d]: %.2f\n",i,val[i]);
++i;
}
}
}
system("Pause");
return 0;
}
/* push: push f onto value stack */
void push(double f)
{
if ( sp < MAXVAL) {
while (val[sp] != '\0')
++sp;
val[sp++] = f;
}
else
printf("error: stack full. can't push %g\n", f);
}
/* pop: pop and return top value from stack */
double pop(void)
{
if (sp > 0) {
double temp = val[--sp];
val[sp] = '\0';
return temp;
}
else {
printf("error: stack empty\n");
return 0.0;
}
}
int getch(void);
void ungetch(int);
/* getop: get next operator or numeric operand */
int getop(char s[])
{
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.')
return c; /* not a number */
i = 0;
if (isdigit(c)) /*collect integer part*/
while (isdigit(s[++i] = c = getch()))
;
if (c == '.') /*collect fraction part*/
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
#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;
}
void print_top(void)
{
int i = 0;
while( val[i] != '\0' )
++i;
--i;
printf("\t%.8g\n",val[i]);
}
Note that I had to move most of my #define statements and prototype declarations to the beginning in order to accommodate for the debugging printfstatement at the end of main().
*Edited out some of my audacious claims :P
You're thinking of the stack backwards - the top of the stack is in the highest valid index, not in val[0]. This behaviour is evident when you look at the pushes of the operands. Your output:
3 4 +
pass #0
val[0]: 3.00
pass #1
val[0]: 3.00
val[1]: 4.00
First, the 3 is pushed - going onto the top of the (previously empty) stack - it's in slot 0. Next 4 is pushed. As you can see, it goes into val[1], clearly showing that val[0] is not the top of the stack in this case.
You're printing the stack incorrectly, which is confusing you further. Change your print loop to:
while (i < sp) {
printf("val[%d]: %.2f\n",i,val[i]);
++i;
}
That is, print only the valid entries in the stack, and you'll see your error.
Your current comparison is looking for a 0 entry on the stack, which isn't how the program is identifying the free entries. That's what the sp variable is used for. In addition to looking for the wrong thing, it's doing it in a bizarro way - val is a an array of floating-point numbers - why are you comparing to a character literal \0?
Here's the complete corrected output:
3 4 +
pass #0
val[0]: 3.00
pass #1
val[0]: 3.00
val[1]: 4.00
pass #2
val[0]: 7.00
7
Now, you see the correct output - both the 3.00 and 4.00 are popped, and 7.00 is pushed back onto the stack. It's now the only valid entry.
Nah. It's just that the stack grows upwards, i. e. val[0] is the bottom (and the top too if there's only one element). And at the time you print the result, val[1] is invalid, it has already been popped.
There is no bug in the code given in K&R for reverse polish calculator.
It works only when the input is given in a single line.
If you press the enter then compiler will read '\n',which results in (case '\n':) of the code and the pop function will be called.
Code result is in the given image:
Edit: I've fixed several things that were pointed out but the real problem remains unsolved.
This bash script:
set -vx
/usr/bin/llvm-gcc-4.2 -ansi -g -o mytest mytest.c
ls -l mytest
./mytest
file mytest
produces this output:
/usr/bin/llvm-gcc-4.2 -ansi -g -o mytest mytest.c
++ /usr/bin/llvm-gcc-4.2 -ansi -g -o mytest mytest.c
ls -l mytest
++ ls -l mytest
-rwxr-xr-x 1 jimslager wheel 37496 May 27 17:26 mytest
./mytest
++ ./mytest
error: unknown command ./mytest
file mytest
++ file mytest
mytest: Mach-O 64-bit executable x86_64
I've extracted this out of something larger that I've been using for several months but have never seen a result like this. How can gcc produce an object with no errors or warnings but the object is unknown?
I'll post test.c if someone asks but it's long and my question seems to me to be independent of what's in test.c.
Edit: Here is the code. Sorry it is so long.
/* Test Exercise 5-10: Write the program myexpr, which evaluates a reverse Polish expression from the command line, where each operator or operand is a separate argument. For example,
* expr 2 3 4 + *
* evaluates 2 x (3+4).
* */
#include <stdio.h>
#include <string.h>
#define MAXLINE 68
#define TABSIZE 8
#define TAB '`'
#define SPACE '-'
#define NEW '\\'
#define TRUE 1
#define FALSE 0
#define IN 1
#define OUT 0
#define FOLDLENGTH 20
#define MAXLEN 12
#define SMALLER(i, j) ((i) > (j) ? (j) : (i))
#define N(c) (c=='\0' ? '\\' : c)
/* #include "subs.c" */
#define MAXOP 100 /* max size of operand or operator */
#define NUMBER '0' /* signal that a number was found */
int getop(char []);
void push(double);
double pop(void);
double top(void);
void dup(void);
void clear(void);
void stackswap(void);
double atof(char s[]);
int myisdigit(char c);
int myisspace(char c);
/* reverse Polish calculator */
int main(int argc, char *argv[])
{
int type;
double op2;
char s[MAXOP];
while (--argc>0)
while (type = getop(&(*++argv[0])))
printf("Just after while: type = %d, *argv[0] = %s\n", type, *argv);
switch (type) {
case NUMBER:
push(atof(*argv));
printf("NUMBER: atof(*argv[0]) = %g, *argv[0] = %s\n", atof(*argv), *argv);
break;
case '+':
printf("+ detected\n");
push(pop() + pop());
break;
case '*':
printf("* detected\n");
push(pop() * pop());
break;
case '-':
printf("- detected\n");
op2 = pop();
push(pop() - op2);
break;
case '/':
printf("/ detected\n");
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor\n");
break;
case '%':
printf("Modulo detected\n");
op2 = pop();
if (op2 != 0.0)
push((int) pop() % (int) op2);
else
printf("error: zero divisor\n");
break;
case 't':
printf("t detected\n");
printf("%g\n", top());
break;
case 'd':
printf("d detected\n");
dup();
break;
case 's':
printf("s detected\n");
stackswap();
break;
case 'c':
printf("c detected\n");
clear();
break;
case '\n':
printf("\\n detected\n");
printf("\t%.8g\n", pop());
break;
default:
printf("error: unknown command %s\n", *argv);
break;
}
return 0;
}
#define MAXVAL 100 /* maximum depth of val stack */
int sp = 0; /* next free stack position */
double val[MAXVAL]; /* value stack */
/* push: push f onto value stack */
void push(double f)
{
printf("push: Started. f = %g, sp = %d\n", f, sp);
if (sp < MAXVAL)
val[sp++] = f;
else
printf("error: stack full, can't push %g\n", f);
printf("push: Finished. f = %g, sp = %d\n", f, sp);
}
/* dup: duplicate top of stack */
void dup(void)
{
printf("dup: Started. top = %g, sp = %d\n", top(), sp);
push(top());
printf("dup: Finished. top = %g, sp = %d\n", top(), sp);
}
/* pop: pop and return top value from stack */
double pop(void)
{
printf("pop: sp = %d, val[--sp] = %g\n", sp, val[sp-1]);
if (sp > 0)
return val[--sp];
else {
printf("error: stack empty\n");
return 0.0;
}
}
/* top: return top value from stack without changing sp */
double top(void)
{
printf("top: sp = %d, val[0] = %g\n", sp, val[0]);
if (sp > 0)
return val[sp-1];
else {
printf("error: stack empty\n");
return 0.0;
}
}
/* stackswap: swap the top 2 values in stack */
void stackswap(void)
{
printf("Starting stackswap: val[sp-1] = %g, val[sp-2] = %g\n", val[sp-1], val[sp-2]);
double op2, op3;
op2 = pop();
op3 = pop();
push(op2);
push(op3);
printf("Finishing stackswap: val[sp-1] = %g, val[sp-2] = %g\n", val[sp-1], val[sp-2]);
}
/* clear: clear the stack */
void clear(void)
{
sp = 0;
}
int getch(void);
void ungetch(int);
/* getop: get next character or numeric operand */
int getop(char s[])
{
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.')
return c; /* not a number */
i = 0;
if (isdigit(c)) /* collect integer part */
while (isdigit(s[++i] = c = getch()))
;
if (c=='.') /* collect fraction part */
while (isdigit(s[++i] = c = getch())) ;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
#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;
}
/* atof: convert s to double */
double atof(char s[])
{
double val, power, epower, d;
int i, j, sign, esign=0, eval;
printf("atof: s = %s\n", s);
for (i = 0; myisspace(s[i]); i++); /* skip white space */
sign = (s[i] == '-') ? -1 : 1; /* Determine sign and strip it */
if (s[i] == '+' || s[i] == '-')
i++;
for (val = 0.0; myisdigit(s[i]); i++) /* Determine value before dec point */
val = 10.0 * val + (s[i] - '0');
if (s[i] == '.')
i++;
for (power = 1.0; myisdigit(s[i]); i++) { /* include value after dec */
val = 10.0 * val + (s[i] - '0');
power *= 10; /* power is where . goes */
}
if (s[i]=='e' || s[i]=='E') { /* Exponential form */
esign = (s[++i]=='-') ? -1 : 1; /* Sign of exponent */
if (s[i]=='+' || s[i]=='-')
i++;
for (epower=0.1, eval=0.0; myisdigit(s[i]); i++) { /* Determine exponent */
eval = 10*eval + (s[i]-'0');
epower *= 10;
}
}
d = (sign*val / power); /* Place dec point in mantissa */
if (esign!=0) { /* If exp form then adjust exponent */
for (j=1; j<=eval; j++) {
d = (esign==1 ? d*10.0 : d/10.0);
}
}
return (d);
}
/* Determine is c is a digit */
int myisdigit(char c)
{
if (c>='0' && c<='9')
return TRUE;
else
return FALSE;
}
/* Returns 1 if c is whitespace, 0 otherwise */
int myisspace(char c)
{
return ((c==' ' || c=='\n' || c=='\t') ? 1 : 0);
}
It should probably be ./test.o to run it. Generally, UNIX systems don't have "." (the current dir) in the PATH by default.
Also, the ".o" extension is slightly misleading, because that is convention for an intermediate object file, not a stand-alone executable like you have produced here.
Make the last line ./test.o and everything should work.
If you only supply the file name but not a path, then only the search paths will be taken into account, and your current working directory in most cases isn't part of them (or rather definitely not, in your example).
. is not in $PATH, so you need to specify the path to the file. ./test.o
.o files are usually not executable; they must be linked before they can be run. Although this is not the case here.
Probably the local directory is not in your path (nor do you want it to be). You should be able to run it with ./test.o. Also, the .o suffix here is strange. You have a dynamically linked executable file, not an object file. (Try file test.o.) On Unix those normally don't have an extension, on Windows they normally have the extension .exe.
Doh! It finally occurred to me what is happening here. This program is exercise 5-10 from K&R which is to revise the Reverse Polish Calculator of page 76 to receive its input from the command line rather than from stdin. I was in the process of doing that when I got the "unknown command" message and thought it was coming from the compiler but it was actually coming from my own code!
Now I will go back and modify it to prefix all error messages with argv[0] (and use this from now on) so that this mistake will never happen again.