Page 1 of 1

Stupid Gremlins :-D

Posted: Sun Jul 13, 2003 7:30 pm
by IntelMole
Okay, this one's just a quickie I promise...

I was looking for some source code to learn a bit more about C, found a fractions calculator and decided I'd have a go at making one myself in the same style, without copying :-D

Got it to the stage where it'll work out the fractions being entered... compiled it... went to sleep.

Woke up, recompiled it to the same point (because I'm paranoid, see earlier thread in Back Porch :-D) and went to run it... only to get it playing infinite loop on me...

Here's the source, the gremlins that be decided it was going to bugger up around second fraction/switch loop time :-D There is more, but it doesn't ever get there.... lol

#include <stdio.h>
#define QUIT 'q'
int nom1 = 0, nom2 = 0, denom1 = 0, denom2 = 0;
int add(), subtract(), multiply(), divide();
int nom1, nom2, denom1, denom2;

main(void)
{
 int choice = 0;
 char quit = 0;

 printf("Enter each fraction in the form numerator/denominator\n");
 printf("Enter the first fraction\n");
 scanf("%d\%d", &nom1, &denom1);

 printf("Enter the second fraction\n");
 scanf("%d\%d", &nom2, &denom2);

do
  {
   printf("\nKey:\n");
   printf("1\t Add\n");
   printf("2\t Subtract\n");
   printf("3\t Multiply\n");
   printf("4\t Divide\n");
   printf("Enter an operation\n");
   scanf("%d", &choice);

   switch (choice)
          {
           case(1):
           Add();
           break;

           case(2):
           Subtract();
           break;

           case(3):
           Multiply();
           break;

           case(4):
           Divide();
           break;

           case(0):
           printf("Enter a valid choice");
           break;

           default:
           printf("Enter a valid choice");
           break;
           }

  } while(choice < 1 || choice > 4);

  while(quit != QUIT);
  {
   printf("\n\nEnter q to quit\n");
   scanf("%c", &quit);
  }
}


I know that it's just something REALLY stupid :-D

But I can't see it, it's staring me right it the face I know it, I must have changed something I think... but I can't for the life of me see it...

Oh, and trying to stare the code out doesn't work either :lol:,
IntelMole[/quote]

Posted: Sun Jul 13, 2003 8:38 pm
by IntelMole
Found it :-D

See the little scanf statements... the damn slashes are the wrong way around!!!!

I recently played with those actually... so I must have just put them in the wrong way...

Whoops, now I feel stupid :-D,
IntelMole

Posted: Sun Jul 13, 2003 8:51 pm
by etilena
Yeah, hate it when I look through lines and lines of code and can't figure out what I did wrong and then notice a little typo. Then I grumble about the half an hour I wasted tryin go fix somethin silly. :wink:

Haha, though I'm getting more proficient and detecting bugs. Debuggers are sometimes really dumb, you have to comment out lines and lines before they point you to the actual mistake. :-?

Posted: Tue Jul 15, 2003 8:41 pm
by IntelMole
My pet hate ATM is writing "print" instead of "printf"... damn VBA screwed me up good :-D But I'm getting used to that one...

Another thing that really annoys me about C is that you have to put a ";" at the end of EVERY line, and I might forget once every 15-20 or so lines... it's like "I know it's supposed to be there, you know it's supposed to be there, so why not just put one there for me!?!?!?!" :-D

Damn quirks,
IntelMole

Posted: Wed Jul 16, 2003 12:41 am
by Craig P.
IntelMole wrote:
My pet hate ATM is writing "print" instead of "printf"... damn VBA screwed me up good :-D


I'd be worried about doing "?" or "?f". :P

Another thing that really annoys me about C is that you have to put a ";" at the end of EVERY line, and I might forget once every 15-20 or so lines... it's like "I know it's supposed to be there, you know it's supposed to be there, so why not just put one there for me!?!?!?!" :-D


Which is great, except for the times when you want to extend to another line. Then again, I do think it's better to use \n as a sentence termination and use continuations when you need them, but it's not my language.

Posted: Wed Jul 16, 2003 6:53 am
by BigMadDrongo
 while(quit != QUIT);
  {
   printf("\n\nEnter q to quit\n");
   scanf("%c", &quit);
  }

This looks wrong - to be precise, the first line. I believe I'm right in saying that that is saying "while quit isn't QUIT, do nothing (i.e. do nothing forever), then when you've finished your infinite loop, print a line and read in 'quit'".

In other words, you don't want a semicolon after the while condition.

Personally I like the C "whitespace and newlines are irrelevant" philosophy. You get used to the semicolons - a line of code now looks wrong to me if it doesn't end with a ; unless I know it's a run-on. (e.g.
if(something)
    do_stuff();

)

Posted: Wed Jul 16, 2003 7:24 am
by zgirl
Wow!!!

My old C instructor would have dragged you out into the street and shot you for the code that you posted. :o

Why?

It is common practice and preferred that you DO NOT write any code in the Main function, unless absolutely necessary.

You always call your functions from Main. Maybe an if statement or two to decide with function to call, but never write code in there other wise.

Posted: Wed Jul 16, 2003 9:35 am
by just brew it!
z-man, it sounds to me like your instructor was incredibly anal.

While I agree it is generally a good idea to keep main() as simple as possible -- i.e. short and to the point -- a blanket "don't put any program logic in main()" rule is just plain silly.

Posted: Wed Jul 16, 2003 9:56 am
by liquidsquid
Agreed, too anal. Your main routine should follow your top level flow chart you have drawn up. In the case of an large embedded system, it may be like this (a bit simplified).

int main(void) {

  initPeripherals();
  initObjectSystem();

  getSettings();

  while(!exitProg) handleTasks();

  return errCode;
}


But that is because there is a large amount of code to pour through under each call, and wouldn't make sense all shoved in main(). However if you have a simple prog like what you have shown, and can all be displayed on one or two sheets of paper, there is no reason to keep everything out of main apart from stealing a few bytes of ram on your stack.

The "keep everything out of main()" mentality is more for documentation reasons than code reasons. On large projects you will tend to organize your code for better readability, and reducing main() is one of the many things you will do.

Also consider a tiny microcontroller with 64 bytes of ram. Do you want to start right off with 2 less, and all those two bytes are holding is the enterance address never to be used again? Silly... Every byte, nibble, and bit counts on these processors. Although programming small controllers in C is overkill, you would normally program in assembly since the code is usually so intimate with the hardware, portability of C is typically non-existant anyway.

-LS

Posted: Thu Jul 17, 2003 9:38 am
by fc34
Theres nothing wrong with:

while (quit != QUIT) theoretically.

you initialized quit as char.

char means either boolean or 1 ASCII character.

QUIT is neither a boolean nor a character.

The reason y u r stuck with the loop is that you are not setting quit to QUIT. In the first place this would be invalid if you tried to do so. But since you are not doing it, the computer jus loops till the condition is true, which will never happen in this case.

You should change it to something like

char temp= ""
//code//

while (temp == "")
{
printf("Press Anykey to quit")
scanf("&c", temp)
}

Posted: Thu Jul 17, 2003 9:52 am
by moog
Unless the quit loop is just for excercise/learning, may I suggest removing the quit loop entirely. IMHO it doesn't serve any purpose except to wait for the user to hit a key (here 'q'). So you could just remove the loop and leave it like this:

printf("\n\nEnter q to quit\n");
scanf("%c", &quit);

You could hit q or anything, doesn't matter, the program exits.

Posted: Thu Jul 17, 2003 11:17 am
by Craig P.
z-man wrote:
It is common practice and preferred that you DO NOT write any code in the Main function, unless absolutely necessary.


I think that's overstating it. You generally want to avoid huge functions that do too much at once, and this will generally mean that for a large program, there won't be anything in main(), but I certainly don't subscribe to a hard rule that there's nothing in main().

Posted: Thu Jul 17, 2003 2:26 pm
by BigMadDrongo
fc34 wrote:
The reason y u r stuck with the loop is that you are not setting quit to QUIT.

But surely he would be, if only that semicolon weren't there?

fc34 wrote:
char temp= ""

I may be wrong, but don't chars use 'single' quotes, and it's strings (insofar as they exist in C) which use "double" quotes?

Posted: Thu Jul 17, 2003 2:54 pm
by liquidsquid
Craig P. wrote:
z-man wrote:
It is common practice and preferred that you DO NOT write any code in the Main function, unless absolutely necessary.


I think that's overstating it. You generally want to avoid huge functions that do too much at once, and this will generally mean that for a large program, there won't be anything in main(), but I certainly don't subscribe to a hard rule that there's nothing in main().


Quite true. My general rule of thumb is if it is more than one page in length, break it up if possible into calls. There are times when it wouldn't make any sense to break it up like a large calculation routine like a FFT or something, but if you are beyond a page in one routine, it is time to see if you can simplify. (One page being around 40 lines of CODE, not comments)

-LS

Posted: Fri Jul 18, 2003 5:05 pm
by Glorious
While I agree it is generally a good idea to keep main() as simple as possible -- i.e. short and to the point -- a blanket "don't put any program logic in main()" rule is just plain silly.


It's definitely overboard, but there is a simple reason for it.

Students (your average student taking C++ programming, not us computer enthusiasts who love tinkering with everything) new to programming often like to stick to what they've already learned, so instead of using functions after being introduced to them, they just continue to do everything in int main() .

So teachers tend to get a little too zealous in telling students to stop doing that, after years of teaching and seeing students do the same thing over and over again, hell, I'd probably make a rule like that too.

Posted: Sat Jul 19, 2003 5:22 pm
by IntelMole
moog wrote:
Unless the quit loop is just for excercise/learning, may I suggest removing the quit loop entirely. IMHO it doesn't serve any purpose except to wait for the user to hit a key (here 'q'). So you could just remove the loop and leave it like this:

printf("\n\nEnter q to quit\n");
scanf("%c", &quit);

You could hit q or anything, doesn't matter, the program exits.


Don't worry, there is a reason. I was planning to put a help key in as well...

FWIW, I also defined QUIT as 'q', but I think QUIT and HELP are more readable than q and h... Personal opinion though, if I was being anal about memory used, i suppose I could change it...

Oh, and the "keep main() clear" issue:
My own opinion is that main should be a program flow controller, so it should call any complex functions e.g. Add(), Subtract(), Cancel() etc. that require more than about 5 lines of code) or any function that needs to bugger with the values of the functions (since it will create temporary versions of these variables, say I needed to square root a number, add it to a variable being pointed to, and then return the value to the original variable, and do some comparison, I'd be more comfortable doing that with temporary variables...)

That's why I've tried to put initial input, and the switch(choice) function in main(), and handed math to other functions...

But hey, I've only been doing this a while, maybe I'll change my own opinion later, but it's my 0.02...

Anyone know of an algorithm to do the highest common factor, for a function Cancel()? My own thought is that it'll be recursive as long as the HCF is >1, possibly using an array similar to the sieve of eratosthenes method in the other post... I'll put my version in and then check back later probably...

Cheers for the input guys,
IntelMole

Posted: Mon Jul 21, 2003 12:02 am
by Craig P.
IntelMole wrote:
Anyone know of an algorithm to do the highest common factor


C or C++?

If C++, check the standard library and check boost. It seems like the sort of thing that would be in one of the two.

Posted: Mon Jul 21, 2003 4:47 pm
by IntelMole
It's being done in vanilla C...

I was kinda thinking of writing it myself though... will probably be along the lines of the following pseudocode:

HCF()
{
   while there's a common factor > 1
   {
      find HCF
      HCF
   }
}


Possibly using some form of array up to the numerator or denominator, whichever is lower...

Thanks anyways,
IntelMole

Posted: Mon Jul 21, 2003 5:52 pm
by moog
... I'm not sure but I'm guessing you want a greatest common denominator function? (like the gcd of 25 and 10 is 5?)

I'll post a non-recursive (gcd1) & recursive version
int gcd1(int min, int max) {
   int rem, tmp;
   if (max < min) {
      tmp = max;
      max = min;
      min = tmp;
   }
   while ((rem = max % min) != 0) {
      max = min;
      min = rem;
   }
   return min;
}

int gcd2(int min, int max) {
   int rem = max % min;
   if (rem == 0)
      return min;
   return gcd2(rem, min);
}

gcd1 has a check to see if max is really the maximum number, gcd2 doesn't (the check is better done before calling gcd2 for efficiency).

These algos are well known, you can google for Euclid's gcd algo. :wink:

Edit: In gcd1, you can replace tmp with rem (get rid of tmp). (I know, useless quibbling is my specialty 8) )

Posted: Mon Jul 21, 2003 6:23 pm
by BigMadDrongo
GCD (greatest common divisor) == GCD (greatest common denominator) == HCF (highest common factor).

private void cancel() {
   if (denom == 1) return;
   long allGCD = coeffs[0], commonDivisor;
   for (int i=1; i<coeffs.length; i++)
       allGCD = euclid(allGCD, coeffs[i]);
   commonDivisor = euclid(allGCD, denom);
   if (commonDivisor < 0) commonDivisor = -commonDivisor;
   if (commonDivisor == 1) return;
   denom /= commonDivisor;
   for (int i=0; i<coeffs.length; i++)
       coeffs[i] /= commonDivisor;
}


The above is from one of our Java assignments, but most of the code except the for loops (I don't believe C has any direct method for finding the length of an array, since it doesn't actually have arrays, so you'll have to store it in a variable or constant beforehand or something) should be valid C as well. It's from a class definition representing univariate polynomials - a set of coefficients (a + bx + cx^2 + ...) stored in the array coeffs, plus a single common denominator denom. (The "euclid" function I use is just Euclid's GCD algorithm, you could use either of the implementations moog posted.)

It won't do exactly what you want, since you're entering a set of fractions each with their own denominator whereas this one shares the denominator, but the two methods are mathematically equivalent and it shouldn't be too hard to modify it to what you want :)

Posted: Mon Jul 21, 2003 6:25 pm
by BigMadDrongo
moog wrote:
Edit: In gcd1, you can replace tmp with rem (get rid of tmp). (I know, useless quibbling is my specialty 8) )

For bonus points ;) - anyone know how you can get rid of tmp without confusingly using rem for the same purpose? (In other words, swap two variables without using any additional variables?)

Posted: Mon Jul 21, 2003 7:25 pm
by murray
x = x - y;
y = x + y;
x = -x + y;

At least, that's all i can come up with on the spot.

Posted: Tue Jul 22, 2003 8:10 am
by moog
For bonus points - anyone know how you can get rid of tmp without confusingly using rem for the same purpose? (In other words, swap two variables without using any additional variables?)

x ^= y;
y ^= x;
x ^= y;

I tend not to use this. I get the feeling this would cause more of a pipeline stall due to dependencies? (I dunno, I'm not an assembler expert) Here let's say registers eax and ebx hold the vals to swap
xor eax, ebx
xor ebx, eax
xor eax, ebx

As opposed to swapping using a temporary reg/mem
mov [tmp], eax
mov eax, ebx
mov ebx, [tmp]

The compiler may use another register instead of mem for the temp var. Anybody know what's better? The xor trick or using a temp var.? (murray's trick also works too, but I think it takes 4 instructions as opposed to 3)

Posted: Tue Jul 22, 2003 4:03 pm
by BigMadDrongo
I've never been much of a low-level programmer, so I couldn't comment on the assembler considerations. (This is why I got fairly irritated when early on in the course our Java lecturer decided to spend fully fifteen minutes explaining an obscure trick about how to reverse a hexadecimal number in Java using the bitwise operators - before he'd so much as shown us a 'for' loop or 'if' statement. Fortunately I knew a decent amount of Java and C already...)

I hadn't seen that negation trick though - neat.

Posted: Tue Jul 22, 2003 4:16 pm
by moog
If you're curious, murray's negation trick will look something like this in assembler (eax = x, ebx = y),

sub eax, ebx
add ebx, eax
neg eax
add eax, ebx

Posted: Tue Jul 22, 2003 11:35 pm
by murray
That XOR trick is pretty spiffy, I hadn't thought of that before.

Only situation I can think of that this sort of thing would really be useful is if you were absolutely positively out of registers and didn't want to force a memory access.