[1+1=2]
OneAndOneIs2

Sun, May 04, 2008

[Link][Icon]K&R exercise 4.3

Finally, I have the time and the inclination both at once to write some C.

4.3 asks you to add the modulus operator % and provision for negative numbers.

The modulus operator takes you back to early schooldays when you hadn't learned about decimals, when 42 divided by 10 was 4 remainder 2. - The modulus gives the remainder, 42 % 10 = 2

I was going to do the modulus in a complicated way: For i % j, I was going to do:
a = i / j
i - (a * j)

So, with the 42 % 10, you'd get:
a = 42/10 = 4
42 - (4 * 10) = 42 - 40 = 2

Which would have worked. But then it occurred to me, What the Hell am I thinking, just use the modulus operator in the code, moron! so I just copied the division code and replaced / with %

Heh.

It didn't work.

invalid operands to binary %

That stumped me.

Then I went back to those schooldays I mentioned earlier. We used 4 remainder 2 because we didn't have decimals. What was the code passing to %? Doubles. Double-precision floating point numbers. Decimal numbers. No remainders.

I've never had to use a cast before. But replacing push(pop() % op2); with push((int) pop() % (int) op2); fixed the problem for me.

Trivial, really.. Such simple little mistakes make for such buggy software :o)

Next, adding negative number support. Tricky. You have to make it differentiate between a - sign meaning a minus number, and a - sign meaning "subtract". Same character, different meaning. Hmmm...

Can't be done in main(), we'll have to differentiate in the getop() function instead.

To start with, obviously, one must change the line that says 'if this isn't a digit or a period, it's not a number, so return it as-is' to instead say 'if this isn't a digit or a period or a minus sign, it's not a number, so return it as-is'

Then you have to decide what to do if it's a minus sign.

First, I was going to go the simple route: If it's a minus sign followed by a space, it means 'subtract', otherwise it's a minus number.

Then I realized that you can have a minus followed by another minus, or a plus, or a divide, or...

Scrub that. We need to do it properly: If the minus sign is followed by a number or a period.

If it is, it's a minus number. If it isn't, it's a subtract. So return a '-'

Oh, and put whatever followed into the buffer, it might be important.

Oh, unless it's an EOF.

Sigh. I didn't catch that last one - the C answer book pointed it out to me. Oh well, live and learn!

All done, I compiled and got an error message. I'd left out a }

Out of practice? Moi?

Fixed it, and it compiled without issue. As test of the new functionality, I decided to try the sum (103 % 10)(-10 - -14) and see if I got 12 as the answer.

(103 % 10)*(-10 - -14)
[(103 % 10) = 3 ] * [(-10 - -14) = (-10 + 14) = 4]
3 * 4 = 12

So, here goes:
$ gcc -Wall polish43.c -o polish
$ ./polish
103 10 % -10 -14 -*
12

Woohoo! :o)

Not the most trouble-free bit of coding I ever did. I'm badly out of practice in thinking through programming logic, and doing things like brackets properly. But I got it all done in the end, which is what counts at the moment...

I also had to look up the sed command I'd aliased as the 'blogify' command on my old laptop to be able to C&P the code to here, so here it is while I remember:
sed -e 's/</\&lt;/g' -e 's/>/\&gt;/g' filename

[More:]

#include <stdio.h>
#include <stdlib.h>

#define MAXOP   100
#define NUMBER  '0'

int getop(char []);
void push(double);
double pop(void);

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: zero divisor\n");
                        break;
                case '%':
                        op2 = pop();
                        if (op2 != 0.0)
                                push((int) pop() % (int) 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;
                }
        }
        return 0;
}

#define MAXVAL  100

int sp = 0;
double val[MAXVAL];

void push(double f)
{
        if (sp < MAXVAL)
                val[sp++] = f;
        else
                printf("error: stack full, can't push %g\n", f);
}

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

int getop(char s[])
{
        int i,c;

        while ((s[0] = c = getch()) == ' ' || c == '\t')
                ;
        s[1] = '\0';
        if (!isdigit(c) && c != '.' && c != '-')
                return c;       /* Not a number */
        i = 0;
        if (c == '-') {         /* Deal with negative numbers */
                if (isdigit(c = getch()) || c == '.')
                        s[++i] = c;
                else {
                        if (c != EOF)
                                ungetch(c);
                        return '-';
                }
        }
        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];
int bufp = 0;

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;
}
Leave a comment • Categories: Omni, Programming

Comments:

No Comments for this post yet...

Leave a comment:

Your email address will not be displayed on this site.
Your URL will be displayed.

Allowed XHTML tags: <p, ul, ol, li, dl, dt, dd, address, blockquote, ins, del, span, bdo, br, em, strong, dfn, code, samp, kdb, var, cite, abbr, acronym, q, sub, sup, tt, i, b, big, small>
(Line breaks become <br />)
(Set cookies for name, email and url)
(Allow users to contact you through a message form (your email will NOT be displayed.))

Categories

May 2008
Mon Tue Wed Thu Fri Sat Sun
 << <   > >>
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Search

Misc

XML Feeds

What is this?
eXTReMe Tracker

Valid XHTML 1.0 Transitional

Valid CSS!

[Valid RSS feed]

powered by
b2evolution

blank