Help with switch statement.

Discussion in 'Mac Programming' started by cybrscot, Jan 31, 2011.

  1. cybrscot macrumors 6502

    cybrscot

    Joined:
    Dec 7, 2010
    Location:
    Somewhere in Southeast Asia
    #1
    Having some trouble with my program, the trouble though is in my scanf. I have troubleshooted it and I know that's the problem.
    if I make my scanf with a single %d and a single variable, everything works fine, But when I make my scanf %d%d, and &a, &b it doesn't work. I need to enter a two digit number, such as (example 94). Then test the first digit in a switch and print a corresponding letter grade. So if the user types 94, the switch tests the first digit, which is 9, and outputs, Letter grade: A.

    But what is happening is that after I enter the two digit 94 in the scanf and hit enter, the program doesn't do anything, but advances to a blank line, then if I type another number or letter and hit enter again, it finally prints the default "F", no matter what numbers I typed. I don't know why it's not executing the switch and printing the corresponding letter grade. As I said, if I delete the second %d, and the b variable, and I just enter a 9, or an 8, or a 7, it works as intended. So all I'm really doing is allowing the user to enter a second variable which really doesn't make any difference to the program, because it is stored in b and not used. I'm treating each number the user inputs as a seperate number, so I should be able to test just the a variable against the cases 9, 8, 7, 6, and the default. I've tried some different things with the scanf, such as %d,%d, and the way it is now, %d%d, but neither work. only scanf ("%d", a) ; has worked so far and correctly tests the a variable in the switch.





    Code:
    #include <stdio.h>
    
    main ()
    
    {
    
    	int a, b ;
    	
    	
    	printf ("Enter Numerical Grade:") ;
    	scanf ("%d%d", &a, &b) ;
    	
    	printf ("Letter Grade: " ) ;
    	
    	switch (a) {
    		case 9: printf ("A\n") ;
    			break;
    		case 8: printf ("B\n") ;
    			break;
    		case 7: printf ("C\n") ;
    			break;
    		case 6: printf ("D\n") ;
    			break;
    		default: printf ("F\n") ;
    			break;
    		}	
    			
    	return 0 ;
    	
    }	
     
  2. Cromulent macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #2
    How about something like this?

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        int a = 0;
        scanf("%d", &a);
    
        printf("Letter grade: ");
        if(a >= 90)
        {
            printf("A\n");
        }
        else if(a < 90 && a >= 80)
        {
            printf("B\n");
        }
        // etc etc
        return EXIT_SUCCESS;
    }
     
  3. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #3
    Or switch on a/10 after ensuring the value is between 0 and 99.

    -Lee
     
  4. Cromulent macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #4
    Ah yes, that is a better solution.
     
  5. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #5
    Or:
    Code:
    char gradeLetters[] = {'F','F','F','F','F','F','D','C','B','A','A'};
    ...
    grade=gradeLetters[a/10];
    With a being between 0 and 100 (works to 109,though).

    -Lee
     
  6. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #6
    This is another case were the approach I used in the wind velocity thread can be used.

    Code:
    #include <stdio.h>
    
    int main(int argc, char *argv) {
    
    	int score;
    	
    	printf ("Enter score (percent): ") ;
    	scanf ("%d", &score ) ;
    
    	int gradeLevel;
    
            gradeLevel = (score >= 60) + (score >= 70) + (score >= 80) + (score >= 90) + (score >= 94);
    
    	switch (gradeLevel) {
    		case 5: printf ("A\n") ;
    			break;
    		case 4: printf ("A-\n") ;
    			break;
    		case 3: printf ("B\n") ;
    			break;
    		case 2: printf ("C\n") ;
    			break;
    		case 1: printf ("D\n") ;
    			break;
    		default: printf ("F\n") ;
    			break;
    		}
    }
    
    I implemented an "A-" case just to show how this gets you away from hard boundaries defined as (score/10) as in the wind case.

    In practice, you'd use an enum here instead of a switch/case as lee1210 points out, but you don't have enums yet.

    EDIT: NOTE your code doesn't handle scores of 100 or single digits. This is a problem with using the %d%d approach you are developing.

    B
     
  7. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #7
    Just to show there's a thousand ways to deal with these things:
    Code:
    char signs[] = {'-','-','-',' ',' ',' ',' ',' ','+','+','+'};
    ...
    sign = signs[a%10];
    
    or using balmw's method:
    Code:
    char signs[] = {'-',' ','+'};
    signPos = (a%10>2) + (a%10>7);
    sign = signs[signPos];
    
    -Lee
     
  8. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #8
    Cats and skins may be separated by any method of your liking. ;)

    The problem with that is the enum which cybrscot's book doesn't introduce until Chapter 16 in the "Advanced" section.

    You'd also need to implement it differently for A and F that don't neatly fall into the usual +/- rules unless you have an A+. (A 49 isn't an F+). :p

    B
     
  9. cybrscot thread starter macrumors 6502

    cybrscot

    Joined:
    Dec 7, 2010
    Location:
    Somewhere in Southeast Asia
    #9



    Balamw is the closest to what the book is asking for, based on where we are in the book. But it's still not exactly what they are looking for. The book's problem(question) is this.

    "Use the switch statement, write a program that converts a numerical grade into a letter grade. Use the following scale: A=90-100, B=80-89, C=70-79, D=60-69, F=0-59. Print an error message if the grade is higher than 100 or less than 0. HINT: Break the grade into two digits, then use a switch statement to test the ten's digit..

    The below code works perfectly, but only allows a user to enter a single digit. I need two digits, and a case for a 100% also, In my previous post I haven't even gotten to the part about the error message, nor have I yet figured out how to get the compiler to distinguish between a grade of 10% and a grade of 100%, since we're comparing the first digit the user enters in the switch, clearly a 10 is an F and a 100 is an A.

    When I have something a bit more complex (for me) I try to get some basic code completed and make sure it works and I'm on the right path before tweaking for the error message, and worrying about a score of 100%. That's why the below code was written, I couldn't get my other code to work. (the one previously posted), so I simplified it to only one %d and a single variable a. It works and produces the correct result. But how can I get it to allow me to enter two digits and work? The non working code makes me input something else after I have already entered a two digit number, then it outputs the grade F as the default, not the correct grade. (maybe that's just my grade for my non working code!! LOL)

    Here is my output from the terminal after compiling and running the program. It just sat on a blank line doing nothing, then I had to enter that 4 you see, then it output the Letter Grade: F. But as you can see it was originally a 94.

    Code:
    Scott-Deans-MacBook-Pro:documents scottdean$ ./a.out
    Enter Numerical Grade:94
    4
    Letter Grade: F
    


    Working Code Example with one %d

    Code:
    #include <stdio.h>
    
    main ()
    
    {
    
    	int a;
    	
    	
    	printf ("Enter Numerical Grade:") ;
    	scanf ("%d", &a) ;
    	
    	printf ("Letter Grade: " ) ;
    	
    	switch (a) {
    		case 9: printf ("A\n") ;
    			break;
    		case 8: printf ("B\n") ;
    			break;
    		case 7: printf ("C\n") ;
    			break;
    		case 6: printf ("D\n") ;
    			break;
    		default: printf ("F\n") ;
    			break;
    		}	
    			
    	return 0 ;
    	
    }	

    Terminal output from this working code, as you can see, 8 gets me a B and matches my switch program for what I was trying to accomplish.


    Code:
    Scott-Deans-MacBook-Pro:documents scottdean$ ./a.out
    Enter Numerical Grade:8
    Letter Grade: B
    
     
  10. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #10
    Add validation code for >100 and < 0. You can either handle 100 specifically as A outside the switch, or just have a case for 10 for the switch. Then just switch on a/10 instead of a. Don't break out the digits in your read, break them out afterwards.

    -Lee
     
  11. talmy macrumors 601

    talmy

    Joined:
    Oct 26, 2009
    Location:
    Oregon
    #11
    I'm surprised nobody pointed out the conceptual flaw in
    Code:
    scanf ("%d%d", &a, &b) ;
    This will read two integer values, not two digits.

    Also, scanf is a pretty poor way to get input. Type "X" to the program and you see why. When I taught C I quickly got to the point where I had the students get input this way:
    Code:
    char buf[128];
    int grade;
    ...
    for (;;) {
        printf("Enter grade:");
        fgets(buf, 128, stdin);
        if (sscanf(buf,"%d",&grade) == 1  // successfully scanned value
            && grade >= 0 && grade <= 100)  { // must be in range
            break;
        }
        printf("Error -- grade must be in range 0 to 100\n");
    }
    
    As far as all the suggested solutions, I'd never use any that are hard coded to any specific grading scale. I'd go with table lookup.
    Code:
    int i;
    struct grade_t {
        int value;
        char *string;
    } grades[] = {
    {0, "F"},      // As many or as few as you want
    {60,"D"},
    {70,"C"},
    {80,"B"},
    {90,"A"},
    {100,"A+"}};
    
    ...
    
    for (i = sizeof(grades)/sizeof(struct grade_t)-1; i > 0; i--) {
        if (grades[i].value < grade) break;
    }
    printf("Grade is %s\n", grades[i].string);
    
    
     
  12. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #12
    Lee and I kind of alluded to it. cybrscot has been using "%1d%1d" in some other code.

    Anyhow, here's my take on it given what cybrscot is supposed to know (which does not yet include, enums, structs or any better way of dealing with inputs) and the hints. Personally I'd use unsigned data types everywhere possible and avoid checking of < 0.

    I really don't like the "separate into digits" approach here since 100 is a perfectly fine score that needs an exception.

    Code:
    #include <stdio.h>
    
    int main(int argc, char *argv) {
    
    	int score;
    	
    	printf ("Enter score (percent): ") ;
    	scanf ("%d", &score ) ;
    
            int ones, tens;
    
            if ((score >= 0) && (score <= 100)) {
    		ones = score % 10;
     	        tens = score / 10;
                   
    		// treat 100 as 99
            	if (score == 100) {
    			ones = 9;
            		tens = 9;
            	}       
    
    		switch (tens) {
    			case 9: printf ("A\n") ;
    				break;
    			case 8: printf ("B\n") ;
    				break;
    			case 7: printf ("C\n") ;
    				break;
    			case 6: printf ("D\n") ;
    				break;
    			default: printf ("F\n") ;
    				break;
    		}
    	}
    	else printf ("ERROR: Score is out of range 0-100\n");
    
    return 0;
    }
    
    B
     
  13. talmy macrumors 601

    talmy

    Joined:
    Oct 26, 2009
    Location:
    Oregon
    #13
    Probably doesn't know about unsigned ints yet either.

    As an early on programming exercise, splitting the value using / and % works fine, but what is irksome to me is that courses tend to not cover good ways to do things. I haven't seen a textbook that devotes any space to user error detection and recovery or program maintainability. Maybe the OP can bookmark this thread and come back to it in a couple months when he can absorb the techniques.

    Just an aside, rather than the test for 100 that changes the grade to 99, just add a "case 10:"
     
  14. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #14
    I was merely taking the hint to its literal absurdity. ;)

    Had the author said "ints" or "numbers" I'd be a lot happier to use a case 10 like you and lee1210 suggest.

    (I can't say, based entirely on following cybrscot's journey, that I'm a huge fan of this textbook, but to each his or her own).

    EDIT: The part that irritates me is that cybrscot generally has good instincts, but the tools to satisfy those instincts are beyond his reach at this point.

    B
     
  15. cybrscot thread starter macrumors 6502

    cybrscot

    Joined:
    Dec 7, 2010
    Location:
    Somewhere in Southeast Asia
    #15
    Thanks for the compliment! As for the book, I found it recommended in many different places, so I thought I'd give it a try.

    Thanks for all the help, although much of it (the others' examples mostly) is "French" to me, it's interesting to see some new things and get exposure to different methods as well as realizing just how many possible ways there are to do something. Especially after hearing all that talk about how computers do "exactly" what you tell them to do, it's interesting to see that there can be so much flexibility. If programmers have a "style", I think I'd like mine to be readability and simplicity. I agree with a prev poster on a different thread that todays hardware is very competent and there is/should be little sacrifice in performance, if any these days for the benefit of readability and simplicity.



    About 6-7 different ways have been thrown out there on how to do it, and I may have to bookmark it as suggested and come back later, but I get really frustrated when I can't get something finished successfully. I feel like I'm cheating if I continue to move ahead to the next thing without completely mastering the concept currently at hand.

    I now see the error in the %d%d. The %d will accept just about as many digits as I throw in there, and once I hit enter it is expecting one more entry. Hence, why it sat there waiting for another number. So I did some tweaking with some things I saw in all the examples and got a bit further.....:D ( I had forgotten how to separate digits, that was a couple chapters ago!!)

    Got the two digit number thing worked out now!! Now I get expected results from a two digit number entered by a user!! Yippee!!!

    Going back to the drawing board to think through the case for 100.... and the book requested "error message" for above 100.

    At the end of each chapter is about 16 or so exercises in some cases, it takes me longer to complete all the exercises than it does to study the chapter!! I'd love to get this one behind me and move on to chapter 6 (loops)! Almost there!


    Code:
    Enter Numerical Grade:67
    Letter Grade: D
    

    Code:
    #include <stdio.h>
    
    main ()
    
    {
    
    	int a, b, tens;
    	
    	
    	printf ("Enter Numerical Grade:") ;
    	scanf ("%d", &a) ;
    	tens = a / 10 ;
    	
    	
    	printf ("Letter Grade: " ) ;
    	
    	switch (tens) {
    		case 9: printf ("A\n") ;
    			break;
    		case 8: printf ("B\n") ;
    			break;
    		case 7: printf ("C\n") ;
    			break;
    		case 6: printf ("D\n") ;
    			break;
    		default: printf ("F\n") ;
    			break;
    		}	
    			
    	return 0 ;
    	
    }	
     
  16. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #16
    The challenge with this bit is that "tens" will be 10 for any number from 100-109. You want to output an "A" for 100, but output an error message (and not also an "F") for any other number. (HINT: What would the code do how if "a" is 120?)

    B
     
  17. cybrscot thread starter macrumors 6502

    cybrscot

    Joined:
    Dec 7, 2010
    Location:
    Somewhere in Southeast Asia
    #17
    Darn, can't get it right. I did eliminate the default case, and specified case's all the way to 0. Which fixed the problem of getting an "F" for values of A above 100

    But I still get an "F" for user input values of < 0.

    In ALL cases of user input whether it be 99 or 100, or 82, I get the error message and the corresponding grade. Why the heck am I getting the error message too? My if statement says if a > 100 or a < 0, then printf "the error message". Clearly 80 is not > 100, nor is it < 0. Even considering that the tens value of 80 is 8, 8 still does not make it true either. I think the if statement is very specific, if it doesn't fall within the specified range, it shouldn't print the error message. Arrrggghhh!!



    Code:
    #include <stdio.h>
    
    main ()
    
    {
    
    	int a, b, tens;
    	
    	
    	printf ("Enter Numerical Grade:") ;
    	scanf ("%d", &a) ;
    	tens = a / 10 ;
    	
    	
    	
    	if (a > 100 || a < 0) ;
    		printf ("error, error, Danger Will Robinson! Danger!!\n") ;
    		
    		
    		
    	printf ("Letter Grade: " ) ;
    		
    	
    	switch (tens) {
    		case 10: printf ("A\n") ;
    			break;
    		case 9: printf ("A\n") ;
    			break;
    		case 8: printf ("B\n") ;
    			break;
    		case 7: printf ("C\n") ;
    			break;
    		case 6: printf ("D\n") ;
    			break;
    		case 5: printf ("F\n") ;
    			break;
    		case 4: printf ("F\n") ;
    			break;
    		case 3: printf ("F\n") ;
    			break;
    		case 2: printf ("F\n") ;
    			break;
    		case 1: printf ("F\n") ;
    			break;
    		case 0: printf ("F\n") ;
    			break;
    		}	
    			
    	return 0 ;
    	
    }	
     
  18. balamw, Jan 31, 2011
    Last edited: Jan 31, 2011

    balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #18
    Wrap everything after the if in an else. ;) (As I did in my last example above).

    i.e. only print a letter grade if you haven't already displayed an error message.

    EDIT: You could have kept the default case, but just had a check in there based on the value of a.

    something like (untested code):

    Code:
    default: {
    if (a==100) printf ("A\n");
    elseif ((a>=0) && (a<=60)) printf ("F\n");
    else printf ("error, error, Danger Will Robinson! Danger!!\n") ;
    break;
    }
    
    B
     
  19. cybrscot thread starter macrumors 6502

    cybrscot

    Joined:
    Dec 7, 2010
    Location:
    Somewhere in Southeast Asia
    #19
    Okay, had an extra semi in my if statement that didn't belong, got rid of that, now any number will give the proper grade, so that's good! But still values between 101 - 109 also give an "A" because of the "tens" problem. Values above 109 print the error message and print "letter grade" also, however no ABCD or F is printed, only "letter grade".




    Code:
    #include <stdio.h>
    
    main ()
    
    {
    
    	int a, b, tens;
    	
    	
    	printf ("Enter Numerical Grade:") ;
    	scanf ("%d", &a) ;
    	tens = a / 10 ;
    	
    	
    	
    	if (a > 100 || a < 0) 
    		printf ("error, error, Danger Will Robinson! Danger!!\n") ;
    		
    		
    		
    	printf ("Letter Grade: " ) ;
    		
    	
    	switch (tens) {
    		case 10: printf ("A\n") ;
    			break;
    		case 9: printf ("A\n") ;
    			break;
    		case 8: printf ("B\n") ;
    			break;
    		case 7: printf ("C\n") ;
    			break;
    		case 6: printf ("D\n") ;
    			break;
    		case 5: printf ("F\n") ;
    			break;
    		case 4: printf ("F\n") ;
    			break;
    		case 3: printf ("F\n") ;
    			break;
    		case 2: printf ("F\n") ;
    			break;
    		case 1: printf ("F\n") ;
    			break;
    		case 0: printf ("F\n") ;
    			break;
    		}	
    			
    	return 0 ;
    	
    }	
     
  20. talmy macrumors 601

    talmy

    Joined:
    Oct 26, 2009
    Location:
    Oregon
    #20
    Code:
    if (a > 100 || a < 0) ;
    		printf ("error, error, Danger Will Robinson! Danger!!\n") ;
    You don't want the semicolon in the first line. The semicolon behaves like an empty statement, so the program will do nothing if the expression is true. Then the statement on the second line will execute no matter what the value of a is.
     
  21. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #21
    I didn't see the errant semicolon as I was looking for the 101-109 problem.

    See my comment above your post. You should only display any grade letter if your input is in range so tie them together with an else.

    NOTE: I prefer this approach over the one I mention above with the default case as it can more easily be converted to a subroutine/function in more complicated cases.

    B
     
  22. talmy macrumors 601

    talmy

    Joined:
    Oct 26, 2009
    Location:
    Oregon
    #22
    Sorry about the simultaneous post about the semi.

    You need an "else" like balamw mentioned.

    if (expression) {
    statements to execute if expression true
    }
    else
    {
    statements to execute if expression false
    }
     
  23. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #23
    in pseudo-code.

    Code:
    if (input is in range)
    {do stuff}
    else
    {do other stuff like throw an error or request new input}
    
    This is a standard process called input or data validation http://en.wikipedia.org/wiki/Data_validation, generally you should have some idea of what form your data will be in and make sure it "fits" or reject it up front.

    B
     
  24. cybrscot thread starter macrumors 6502

    cybrscot

    Joined:
    Dec 7, 2010
    Location:
    Somewhere in Southeast Asia
    #24
    Finally!! This code works perfectly!!!! What do you think?? I'm going to bed now, 4 a.m. again as usual!! Thanks everybody for the help! Can anyone tell me why some of you put "void" after the main ()?????


    Code:
    #include <stdio.h>
    
    main ()
    
    {
    
    	int a, b, tens;
    	
    	
    	printf ("Enter Numerical Grade:") ;
    	scanf ("%d", &a) ;
    	
    	
    	
    	
    	if (a > 100 || a < 0) {
    		printf ("error, error, Danger Will Robinson! Danger!!\n") ;
    		return 0 ;
    		}
    		
    		
    	printf ("Letter Grade: " ) ;
    		tens = a / 10 ;
    	
    	switch (tens) {
    		case 10: printf ("A\n") ;
    			break;
    		case 9: printf ("A\n") ;
    			break;
    		case 8: printf ("B\n") ;
    			break;
    		case 7: printf ("C\n") ;
    			break;
    		case 6: printf ("D\n") ;
    			break;
    		case 5: printf ("F\n") ;
    			break;
    		case 4: printf ("F\n") ;
    			break;
    		case 3: printf ("F\n") ;
    			break;
    		case 2: printf ("F\n") ;
    			break;
    		case 1: printf ("F\n") ;
    			break;
    		case 0: printf ("F\n") ;
    			break;
    		}	
    			
    	return 0 ;
    	
    }	
     
  25. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #25
    It just makes it explicit that this function doesn't use any arguments, just like many of us are explicit in writing
    Code:
    int main
    instead of just main even though int is assumed.

    Note it's generally better to keep track of a return value and have only a single return. In this case you should return something other than zero if the error message was triggered.

    B
     

Share This Page