Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.
Code:
		first_number = number / 10 ;
	    second_number = number % 10 ;
	
	switch (first_number) {
		case 9: printf ("ninety"); break;
		case 8: printf ("eighty"); break;
		case 7: printf ("seventy"); break;
		case 6: printf ("sixty"); break;
		case 5: printf ("fifty"); break;
		case 4: printf ("forty"); break;
		case 3: printf ("thirty"); break;
		case 2: printf ("twenty"); break;
		}
		
	switch (second_number) {
		case 9: printf ("-nine\n"); break;
		case 8: printf ("-eight\n"); break;
		case 7: printf ("-seven\n"); break;
		case 6: printf ("-six\n"); break;
		case 5: printf ("-five\n"); break;
		case 4: printf ("-four\n"); break;
		case 3: printf ("-three\n"); break;
		case 2: printf ("-two\n"); break;
		case 1: printf ("-one\n"); break;
		case 0: printf ("\n"); break;
		}

My suggestion, which naples98 was clarifying, was to change that into something like:

Code:
	first_number = number / 10 ;
	second_number = number % 10 ;
	
	switch (first_number) {
		case 9: printf ("ninety"); break;
		case 8: printf ("eighty"); break;
		case 7: printf ("seventy"); break;
		case 6: printf ("sixty"); break;
		case 5: printf ("fifty"); break;
		case 4: printf ("forty"); break;
		case 3: printf ("thirty"); break;
		case 2: printf ("twenty"); break;
		}

	if(first_number != 0 && second_number != 0) {
		printf("-");
	}

	switch (second_number) {
		case 9: printf ("nine"); break;
		case 8: printf ("eight"); break;
		case 7: printf ("seven"); break;
		case 6: printf ("six"); break;
		case 5: printf ("five"); break;
		case 4: printf ("four"); break;
		case 3: printf ("three"); break;
		case 2: printf ("two"); break;
		case 1: printf ("one"); break;
		}
	printf("\n");

This also incorporates balmw's idea re: the - and single digit numbers.

Also, you might want to add some error checking as we have discussed previously. If the user enters -234 or 2114 the code won't work properly. You ask them to enter a 2 digit number. If they disobey (maybe you can be lenient about a 1 digit number) you should tell them so. Just check if what's entered is < 1 or > 99. You didn't specify the number must be positive, but obviously the code only handles positives right now.

-Lee

Aha! okay, I get it. I'm going to write that one down to remember how to handle it. Thanks for showing me. Sometimes it's better because I haven't come to it in the book yet, so I'm happy to learn some things that can compliment the code I've already written. As I have a deep understanding of what I've written because of the time and thought involved, it's easy to pick up a pointer or two of new information that I can incorporate.

Gee, if only we could have written computer programming before inventing the English language, we could have come up with an English language that would be easier to code!!
 
Gee, if only we could have written computer programming before inventing the English language, we could have come up with an English language that would be easier to code!!

In another few weeks do this in another spoken language for kicks. I.e. in German 21 is one-and-twenty (einundzwanzig), and it still has the weird cases for 11 and 12 (elf and zwolf), though 10 and 13-19 are pretty similar to how other two digit numbers are composed.

Then a few weeks after that do this to handle more digits and negatives. Once you get to looping dealing with 1,532,125,531 should be no problem. Negatives would just take one tiny extra bit of code.

-Lee
 
For completeness, here is a revised version of the code I posted in post 39 https://forums.macrumors.com/posts/11826728/ revised to take in some of the changes lee proposed and other things that came up in the discussion.

It uses only things you should know at this point from the book.

Code:
#include <stdio.h>

int main(void) {

  int number;

  //Use intermediate variables to make code read more easily
  int isinrange, isspecial, needsdash; 
        
  printf ("Enter two digit number (00-99): ") ;
  scanf ("%d", &number ) ;  

  isinrange = (number >= 0) && (number <= 99);
  isspecial = (number >= 10) && (number <= 19);
  if (isinrange) {
    int ones, tens;
    
    //I've got a good number in-range so start the output
    printf ("You entered the number ", number) ;  
    
    ones = number % 10;
    tens = number / 10;
    
    needsdash = ((tens != 0) && (ones != 0));
        
    // handle the special cases (10-19)
    if (isspecial) {
      switch (ones) {
      case 9: printf ("nineteen"); break;
      case 8: printf ("eighteen"); break;
      case 7: printf ("seventeen"); break;
      case 6: printf ("sixteen"); break;
      case 5: printf ("fifteen"); break;
      case 4: printf ("fourteen"); break;
      case 3: printf ("thirteen"); break;
      case 2: printf ("twelve"); break;
      case 1: printf ("eleven"); break;
      case 0: printf ("ten"); break;
      }
      }
      //handle the special case zero
      else if (number == 0) printf ("zero");
      //handle standard two-digit number
      else { 
        switch (tens) {
        case 9: printf ("ninety"); break;
        case 8: printf ("eighty"); break;
        case 7: printf ("seventy"); break;
        case 6: printf ("sixty"); break;
        case 5: printf ("fifty"); break;
        case 4: printf ("forty"); break;
        case 3: printf ("thirty"); break;
        case 2: printf ("twenty"); break;
        case 1: break;
        case 0: break;
        }

        if (needsdash) printf("-");
                
        switch (ones) {
        case 9: printf ("nine"); break;
        case 8: printf ("eight"); break;
        case 7: printf ("seven"); break;
        case 6: printf ("six"); break;
        case 5: printf ("five"); break;
        case 4: printf ("four"); break;
        case 3: printf ("three"); break;
        case 2: printf ("two"); break;
        case 1: printf ("one"); break;
        case 0: break;
        }        
      }
  // Finish the output
  printf("\n");
  }
  //Output an error message if input was out of range
  else printf ("ERROR: Score is out of range 0-100\n"); 

  return 0;
}

I refer you once again to the xkcd cartoon on how to write good code (https://forums.macrumors.com/threads/1076009/)

good_code.png


More often than not, you throw it out and start over again.

EDIT: Note the crude attempts and making the if statements readable by using intermediate variables.

B
 
For completeness, here is a revised version of the code I posted in post 39 https://forums.macrumors.com/posts/11826728/ revised to take in some of the changes lee proposed and other things that came up in the discussion.

It uses only things you should know at this point from the book.

Code:
#include <stdio.h>

int main(void) {

  int number;

  //Use intermediate variables to make code read more easily
  int isinrange, isspecial, needsdash; 
        
  printf ("Enter two digit number (00-99): ") ;
  scanf ("%d", &number ) ;  

  isinrange = (number >= 0) && (number <= 99);
  isspecial = (number >= 10) && (number <= 19);
  if (isinrange) {
    int ones, tens;
    
    //I've got a good number in-range so start the output
    printf ("You entered the number ", number) ;  
    
    ones = number % 10;
    tens = number / 10;
    
    needsdash = ((tens != 0) && (ones != 0));
        
    // handle the special cases (10-19)
    if (isspecial) {
      switch (ones) {
      case 9: printf ("nineteen"); break;
      case 8: printf ("eighteen"); break;
      case 7: printf ("seventeen"); break;
      case 6: printf ("sixteen"); break;
      case 5: printf ("fifteen"); break;
      case 4: printf ("fourteen"); break;
      case 3: printf ("thirteen"); break;
      case 2: printf ("twelve"); break;
      case 1: printf ("eleven"); break;
      case 0: printf ("ten"); break;
      }
      }
      //handle the special case zero
      else if (number == 0) printf ("zero");
      //handle standard two-digit number
      else { 
        switch (tens) {
        case 9: printf ("ninety"); break;
        case 8: printf ("eighty"); break;
        case 7: printf ("seventy"); break;
        case 6: printf ("sixty"); break;
        case 5: printf ("fifty"); break;
        case 4: printf ("forty"); break;
        case 3: printf ("thirty"); break;
        case 2: printf ("twenty"); break;
        case 1: break;
        case 0: break;
        }

        if (needsdash) printf("-");
                
        switch (ones) {
        case 9: printf ("nine"); break;
        case 8: printf ("eight"); break;
        case 7: printf ("seven"); break;
        case 6: printf ("six"); break;
        case 5: printf ("five"); break;
        case 4: printf ("four"); break;
        case 3: printf ("three"); break;
        case 2: printf ("two"); break;
        case 1: printf ("one"); break;
        case 0: break;
        }        
      }
  // Finish the output
  printf("\n");
  }
  //Output an error message if input was out of range
  else printf ("ERROR: Score is out of range 0-100\n"); 

  return 0;
}

I refer you once again to the xkcd cartoon on how to write good code (https://forums.macrumors.com/threads/1076009/)

good_code.png


More often than not, you throw it out and start over again.

EDIT: Note the crude attempts and making the if statements readable by using intermediate variables.

B


Ahh very nice, beautifully written. Thanks, nice cartoon too. Got a giggle from that.
 
For completeness, here is a revised version of the code I posted in post 39 https://forums.macrumors.com/posts/11826728/ revised to take in some of the changes lee proposed and other things that came up in the discussion.

It uses only things you should know at this point from the book.

Code:
#include <stdio.h>

int main(void) {

  int number;

  //Use intermediate variables to make code read more easily
  int isinrange, isspecial, needsdash; 
        
  printf ("Enter two digit number (00-99): ") ;
  scanf ("%d", &number ) ;  

  isinrange = (number >= 0) && (number <= 99);
  isspecial = (number >= 10) && (number <= 19);
  if (isinrange) {
    int ones, tens;
    
    //I've got a good number in-range so start the output
    printf ("You entered the number ", number) ;  
    
    ones = number % 10;
    tens = number / 10;
    
    needsdash = ((tens != 0) && (ones != 0));
        
    // handle the special cases (10-19)
    if (isspecial) {
      switch (ones) {
      case 9: printf ("nineteen"); break;
      case 8: printf ("eighteen"); break;
      case 7: printf ("seventeen"); break;
      case 6: printf ("sixteen"); break;
      case 5: printf ("fifteen"); break;
      case 4: printf ("fourteen"); break;
      case 3: printf ("thirteen"); break;
      case 2: printf ("twelve"); break;
      case 1: printf ("eleven"); break;
      case 0: printf ("ten"); break;
      }
      }
      //handle the special case zero
      else if (number == 0) printf ("zero");
      //handle standard two-digit number
      else { 
        switch (tens) {
        case 9: printf ("ninety"); break;
        case 8: printf ("eighty"); break;
        case 7: printf ("seventy"); break;
        case 6: printf ("sixty"); break;
        case 5: printf ("fifty"); break;
        case 4: printf ("forty"); break;
        case 3: printf ("thirty"); break;
        case 2: printf ("twenty"); break;
        case 1: break;
        case 0: break;
        }

        if (needsdash) printf("-");
                
        switch (ones) {
        case 9: printf ("nine"); break;
        case 8: printf ("eight"); break;
        case 7: printf ("seven"); break;
        case 6: printf ("six"); break;
        case 5: printf ("five"); break;
        case 4: printf ("four"); break;
        case 3: printf ("three"); break;
        case 2: printf ("two"); break;
        case 1: printf ("one"); break;
        case 0: break;
        }        
      }
  // Finish the output
  printf("\n");
  }
  //Output an error message if input was out of range
  else printf ("ERROR: Score is out of range 0-100\n"); 

  return 0;
}

I refer you once again to the xkcd cartoon on how to write good code (https://forums.macrumors.com/threads/1076009/)

good_code.png


More often than not, you throw it out and start over again.

EDIT: Note the crude attempts and making the if statements readable by using intermediate variables.

B
Or by DeMorgan's law :):
Code:
 needsdash = !(tens == 0 || ones == 0);
 
Or by DeMorgan's law :):
Code:
 needsdash = !(tens == 0 || ones == 0);

I have to think about what that means, i know immediately with the version with &&. Certainly knowing how to transform logical expressions is very important, and sometimes a terrible mess can be made very elegant using transformations like this. This particular case isn't one of them, in my opinion.

-Lee
 
Code:
    needsdash = ((tens != 0) && (ones != 0));

While it won't print the dash due to how you structured your program, your logic for needsdash will evaluate to true for numbers 11-19 when they don't need a dash.

It should be

Code:
needsdash = ((tens > 1) && (ones != 0));
 
While it won't print the dash due to how you structured your program, your logic for needsdash will evaluate to true for numbers 11-19 when they don't need a dash.

True, there is an implicit "isstandard" i.e. (!(isspecial ||(number==0))) assumed by the structure of the code for the needsdash condition.

If this was real life, I'd implement the whole standard case handler as its own function and needsdash would be local to that, so it would be even clearer that 11-19 are not covered by it. (Of course it's not real life since you'd probably use simple arrays to replace the switch/case/break statements).

B
 
I have to think about what that means, i know immediately with the version with &&. Certainly knowing how to transform logical expressions is very important, and sometimes a terrible mess can be made very elegant using transformations like this. This particular case isn't one of them, in my opinion.

-Lee
In C, "!(p || q)" means "neither p nor q)." The "(p || q)" expression is logically equivalent to (!p & !q). Anytime !(p ||q) is true, so is (!p && !q). Negate only the p in "p || q" to get "!p || q," and you're change the expression into "(!p) || q".

Maybe "!p && !q" is more readable than "!(p || q)." But "!(p || q)" is simpler than "(!p && !Q)" because "!(p || q)" contains only one "!."

See? I told ya I specialized in logic when I was earning my philosophy degree. I can even write a truth table. Imagine that. :)

For an even cheaper thrill, remember that ((!p) || q) is logically equivalent to "If p, then q".
 
Last edited:
But "!(p || q)" is simpler than "(!p && !Q)" because "!(p || q)" contains only one "!."
"Simpler" doesn't necessarily mean higher performance as they'll probably get optimized to the exact same assembly code by the compiler with optimization on. Sometimes "simpler to maintain" is better.

Maybe once it did matter, but today it is far less important to try to pre-optimize your code for the compiler unless you are in the middle of a very tight, oft repeated loop.

B
 
I just "borrowed" lee's code for that, but fine if you prefer.

No need to quote the whole post including the image just for that tho.

B
Oops! I should have deleted much of the quoted text. But maybe the whole quotation makes !(tens || ones) surprising.
 
In C, "!(p || q)" means "neither p nor q)." The "(p || q)" expression is logically equivalent to (!p & !q). Anytime !(p ||q) is true, so is (!p && !q). Negate only the p in "p || q" to get "!p || q," and you're change the expression into "(!p) || q".

Maybe "!p && !q" is more readable than "!(p || q)." But "!(p || q)" is simpler than "(!p && !Q)" because "!(p || q)" contains only one "!."

See? I told ya I specialized in logic when I was earning my philosophy degree. I can even write a truth table. Imagine that. :)

For an even cheaper thrill, remember that ((!p) || q) is logically equivalent to "If p, then q".

My case was this particular transformation did not aid in ease of reading in this case. That's my opinion. I was not arguing that you had transformed the expression improperly or that C's logical operators acted differently than one might expect. Hopefully others benefitted from your explanation, but despite my relative inexperience with philosophy compared to you, I did take a few philosophy courses (some covering formal logic, some ethics) while pursuing my CS degree.

-Lee
 
My case was this particular transformation did not aid in ease of reading in this case. That's my opinion. I was not arguing that you had transformed the expression improperly or that C's logical operators acted differently than one might expect. Hopefully others benefitted from your explanation, but despite my relative inexperience with philosophy compared to you, I did take a few philosophy courses (some covering formal logic, some ethics) while pursuing my CS degree.

-Lee
I doubt that my transformation improves program readability, and I'm sure many of us here have taken some philosophy courses. I wasn't showing off. And I certainly wasn't looking down on anyone. I the first post where I typed "!(p || q)", I grinned. Unfortunately, I sometimes sound like a show-off when I don't mean to do that.
 
Last edited:
Isn't that a glorified way of saying

Code:
needsdash = tens && ones;
?

Sure, and I'd be willing to bet they compile to the same thing, but the meaning of the first form is readily apparent, while the second would at least deserve a comment. i.e.

Code:
\\I need a dash of both digits are non-zero
needsdash = tens && ones;

Yes, but who expects the words "tens" and "ones" to stand for truth-values?

I do tend to blur the distinction in C, as evidenced in other threads, but in this case I do think lee's original construct is the easiest to suss out the intent at a glance.

cybrscot, just curious - are you still finding this thread useful?

;) I hope he's moved on by now and will come back to it in a few weeks/months.

B
 
Actually, it will negate the result of the expression (p || q). So, if p =1 and q = 0 then !(p || q) is false.
It's one thing to say that !(p || q) means "nether p nor q." It's something else to say !(p || q) is only logically equivalent to "neither p nor q." But whether !(p || q) means "neither p nor q" is different from whether p, q, or both are actually false. I'm not talking about what happens at runtime. I'm talking about logical relationships among boolean expressions. One and zero aren't true and false. They represent truth and falsehood. Subsonix and I are reasoning at different levels of abstraction.
 
Last edited:
It's one thing to say that !(p || q) means "nether p nor q." It's something else to say !(p || q) is only logically equivalent to "neither p nor q." But whether !(p || q) means "neither p nor q" is different from whether p, q, or both are actually false. I'm not talking about what happens at runtime. I'm talking about logical relationships among boolean expressions.

I wasn't arguing, just wording it in a different way. The only condition where (p || q) is false is if p = 0 and q = 0 or "neither p nor q". This then becomes the only condition that is true for !(p || q). "neither p nor q", as you said.

Code:
#include <stdio.h>

int main()
{
        int p = 0;
        int q = 0;

        printf("%s\n", !(p || q) ? "true" : "false");


        return 0;
}
 
EDIT: Note the crude attempts and making the if statements readable by using intermediate variables.

B

Oh, so you pick your variable words so that they fit into the code in such a way that when the code is read, it completes the thought or idea, thus further hinting at what your trying to accomplish without comments necessarily, although comments are still used of course. But I get it, I never thought of that, or hadn't yet! Thanks for the tip!
 
I wasn't arguing, just wording it in a different way. The only condition where (p || q) is false is if p = 0 and q = 0 or "neither p nor q". This then becomes the only condition that is true for !(p || q). "neither p nor q", as you said.

Code:
#include <stdio.h>

int main()
{
        int p = 0;
        int q = 0;

        printf("%s\n", !(p || q) ? "true" : "false");


        return 0;
}
Yes, I agree with you. After all, C's "||" means "inclusive or." I should have thought more carefully about what you said, subsonix.
 
Last edited:
"Simpler" doesn't necessarily mean higher performance as they'll probably get optimized to the exact same assembly code by the compiler with optimization on. Sometimes "simpler to maintain" is better.

Maybe once it did matter, but today it is far less important to try to pre-optimize your code for the compiler unless you are in the middle of a very tight, oft repeated loop.

B
I plan to write a program that uses both expressions to see whether the machine converts one to the other.

Sometimes optimizing a program by hand prevents some optimizations at runtime. Before I read a book about how to pre-optimize a program, I thought optimizing by hand would always be a good thing to do.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.