Suprisingly small limits of the calculator written in C - c

My task is to write a calculator, which accepts only valid operands, that is, * / + - ^. The calculator has to evaluate (check the validity) of the line being entered. For example, it accepts only lines of the following form: 20 + 9, 8 ^ 2, etc. If someone entered 9y, 20+2 or exit, the expression would be evaluated to 0. Then the user has to have the possibility of displaying lines, which were entered, regardless of their validity. (Switch E for invalid lines, switch V for valid lines, if valid line is entered and V is chosen it also displays the value of an expression).
Let's now consider the example in order to see how it works.
Line: 20 + 20
Switch: E
Output: There's nothing wrong with the line 20 + 20.
Switch: V
Output: The result is 40.
Line: 20r
Switch: E:
Output: The line is 20r
Switch: V:
Output: The line 20r is invalid.
The problem:
Every time I enter a line, which would have the result of calculation more then, I guess 7000 (it doesn't work for 8 * 900), it doesn't even allow me to choose the char for the switch. It displays the line Choose E for... and then automatically You haven't chosen the valid option of the switch..
MAIN.C:
#include "stdio.h"
#include "evalexpression.h"
int main() {
char string[100];
int result;
result = InterCalc(string);
CalcFilter(result, string);
return 0;
}
EVALEXPRESSION.C
#include "stdio.h"
#include "string.h"
#include "evalexpression.h"
#include "math.h"
#include "float.h"
static float f1, f2;
static char op;
int isValidExpression(const char *str) {
int res;
char ops[10];
res=sscanf(str, "%f %s %f", &f1, ops, &f2);
if (res==3) {
if (ops[0]=='+' || ops[0]=='-' || ops[0]=='^' || ops[0]=='*' || ops[0]=='/') {
op=ops[0];
return 1;
}
else
return 0;
}
else
return 0;
}
int getOperator() {
if (op=='+')
return 1;
else if (op=='-')
return 2;
else if (op=='/')
return 3;
else if (op=='*')
return 4;
else if (op=='^')
return 5;
else return 0;
}
float getFstOperand() {
return(f1);
}
float getSecOperand() {
return(f2);
}
int InterCalc(char *my_string) {
fgets(my_string, sizeof(my_string), stdin);
if (strcmp(my_string, "exit\n")==0 ) {
printf("Program ended\n");
return 0;
}
else if ( isValidExpression(my_string) == 0 ) {
printf("Expression error\n");
return 0;
}
else
return 1;
}
float getExprValue() {
int operation;
operation = getOperator();
switch (operation) {
case 1:
return (getFstOperand() + getSecOperand());
break;
case 2:
return (getFstOperand() - getSecOperand());
break;
case 3:
return (getFstOperand() / getSecOperand());
break;
case 4:
return (getFstOperand() * getSecOperand());
break;
case 5:
return ( pow(getFstOperand(), getSecOperand()) );
break;
default:
return 0;
}
}
void CalcFilter(int a, char *str) {
float calculation_value;
printf("Press 'E' to display the invalid line or press 'V' to display the valid line\n");
int choice;
choice = getchar();
switch (choice) {
case 'E':
case 'e':
if (a==0)
printf("The line %s is invalid.\n", str);
else if (a==1)
printf("There's nothing wrong with the line %s\n", str);
break;
case 'V':
case 'v':
if (a==1) {
calculation_value = getExprValue();
//if (calculation_value > FLT_MAX || calculation_value < FLT_MIN)
// printf("The value of expression is beyond limits\n");
//else
printf("The result of %s is %f.\n", str, calculation_value); }
if (a==0)
printf("The line %s is invalid\n", str);
break;
default:
printf("You haven't chosen the valid option of the switch\n");
break;
}
}

Look at the first lines of your InterCalc() function
int InterCalc(char *my_string) {
fgets(my_string, sizeof(my_string), stdin);
sizeof is a compile-time operator. sizeof(my_string) is the size of a pointer to char which for a 32 bit system is typically 4 bytes.
sizeof does not give the length of whatever string my_string points at. It will not give 100 (the length of the array in main()).
You will need to pass the value 100 (or whatever the size is in main() if you change it) by some other means, such as an extra function parameter.

Related

Changing an integer grade into a character grade using a switch statement

Using the switch statement, write a program that converts a numerical grade into a letter
grade:
Enter numerical grade: 84
Letter grade: B
Use the following grading scale:
A = 90-100, B = 80-89, C = 70-79, D = 60-69, F = 0-59.
Print an error message if the grade is larger than 100 or less than 0.
Hint: Break the grade into two digits, then use a switch statement to test the ten's digit.
How can I change the code to let 100 become A? I input 100 and the output was F.
#include <stdio.h>
int main(void)
{
int front_number, back_number;
printf("Enter numerical grade: ");
scanf("%1d%1d", &front_number, &back_number);
printf("Letter grade: ");
switch (front_number){
case 0: case 1: case 2: case 3: case 4: case 5:
printf("F");
break;
case 6:
printf("D");
break;
case 7:
printf("C");
break;
case 8:
printf("B");
break;
case 9:
printf("A");
break;
}
return 0;
}
In a real world program you would likely create a struct per grade, then search through a table of read-only elements. Example:
#include <stdio.h>
typedef struct
{
int low_limit;
int high_limit;
char grade;
} grade_t;
int main (void)
{
const grade_t grade[] =
{
{ 0, 59, 'F' },
{ 60, 69, 'D' },
{ 70, 79, 'C' },
{ 80, 89, 'B' },
{ 90, 100, 'A' },
};
int input = 82;
for(size_t i=0; i<sizeof grade/sizeof *grade; i++)
{
if(input <= grade[i].high_limit)
{
printf("%d gives %c", input, grade[i].grade);
break;
}
}
}
This is a naive linear search method but since the amount of items is so limited, it's likely quite efficient. Alternatively you could use standard C bsearch, in case you have a larger amount of sorted data.
Notably, we don't even need to know the lower limit since that's implicit by the previous item in the array.
It is better to not read two digits, because:
This tries to read the input and process this input in one step, while these are separate processes.
If the input is 100, it will leave the last 0 character in the input stream.
The cases 10 and 100 are indistinguishable.
The hint to "break the grade into two digits" is misleading for similar reasons.
The (probably) intended solution
Here is, what I assume to be, more or less the intended solution. It reads the entire (unsigned) integer, and uses a switch for its value divided by 10. The one not-so-elegant part is the default case (more of an "else" here) which has to distinguish between valid 3-digit input (i.e. 100 and invalid input.
Technically, part of the input is now being handled by an if-statement instead of a switch, but this is the most sensible solution given the problem statement. A bit better would be to catch the edge case grade == 100 at the top of function and leave the default case to only return an error value.
#include <stdio.h>
#include <stdlib.h>
char gradeUintToChar(unsigned int grade);
int main(void)
{
printf("Enter numerical grade: ");
fflush(stdout);
unsigned int gradeUint;
if (scanf("%u", &gradeUint) != 1)
{
fprintf(stderr, "Invalid input\n");
exit(EXIT_FAILURE);
}
char gradeChar = gradeUintToChar(gradeUint);
if (gradeChar == 0)
{
fprintf(stderr, "gradeUintToChar: Invalid parameter\n");
exit(EXIT_FAILURE);
}
printf("Letter grade: %c\n", gradeChar);
}
char gradeUintToChar(unsigned int grade)
{
switch (grade / 10U)
{
case 0: case 1: case 2: case 3: case 4: case 5:
return 'F';
case 6:
return 'D';
case 7:
return 'C';
case 8:
return 'B';
case 9:
return 'A';
default:
if (grade == 100U)
return 'A';
else
return (char) 0; // Indicates failure
}
}
The GNU case ranges solution
There is a GNU extension called case ranges which allows for this to be implemented rather directly using a switch. Since this is non-standard C, this is generally not a recommended method.
Code example:
#include <stdio.h>
#include <stdlib.h>
char gradeUintToChar(unsigned int grade);
int main(void)
{
printf("Enter numerical grade: ");
fflush(stdout);
unsigned int gradeUint;
if (scanf("%u", &gradeUint) != 1)
{
fprintf(stderr, "Invalid input\n");
exit(EXIT_FAILURE);
}
char gradeChar = gradeUintToChar(gradeUint);
if (gradeChar == 0)
{
fprintf(stderr, "gradeUintToChar: Invalid parameter\n");
exit(EXIT_FAILURE);
}
printf("Letter grade: %c\n", gradeChar);
}
char gradeUintToChar(unsigned int grade)
{
switch (grade)
{
case 0 ... 59:
return 'F';
case 60 ... 69:
return 'D';
case 70 ... 79:
return 'C';
case 80 ... 89:
return 'B';
case 90 ... 100:
return 'A';
default:
return (char) 0; // Indicates failure
}
}

Why do I get, variable is not initialized error?

#include <stdio.h>
#include <conio.h>
void Calculator();
void main()
{
Calculator();
getch();
}
void Calculator()
{
int n,j;
char f1;
double t;
printf("please enter two numbers");
scanf("%d%d",&n,&j);
printf("please enter the syboml you want ( * / + or -)");
scanf("%c",&f1);
if( f1 == '+')
t = n + j;
if (f1 == '-')
t = n-j;
if (f1 == '*')
t = n*j;
if (f1 == '/')
t = n/j;
printf("%f" ,t);
}
In your final printf, you are using t that has never been initialized, and might hold a garbage value if no-one of those if conditions is met.
Consider initializing t (a simple = 0 does the job) or add an else clause somewhere
Edit:
While I was at it, I also made some changes to make sure the second scanf ignores the trailing /n without using fflush.
Edit 2:
As suggested by HAL9000, assuming that an initialization to 0 would be enough is wrong. I modified the second part of the program to make use of a switch-case and eventually reject an invalid operator.
The final code looks like this
#include <conio.h>
#include <stdio.h>
void Calculator();
int main() {
Calculator();
getch();
return 0;
}
void Calculator() {
int n, j;
char f1;
double t;
printf("please enter two numbers: ");
scanf("%d%d", &n, &j);
printf("please enter the symbol you want ( * / + or -): ");
scanf(" %c", &f1);
switch (f1) {
case '+':
t = n + j;
break;
case '-':
t = n - j;
break;
case '*':
t = n * j;
break;
case '/':
t = (float)n / j;
break;
default:
printf("Invalid symbol, please use ( * / + or -)\n");
return;
}
printf("%f\n", t);
}
In some compilers, you might be getting this t is not initialized error as your code will never ask please enter the symbol you want ( * / + or -) because scanf("%c",&f1); will take input as trailing newline char, so t never gets initialized. I ran your code in GCC compiler on mac but got output as 0.0000 as t will never be initialized in your case.
You can just eat up the trailing char for that you can use getchar(); or you can also put a space in the format string, e.g. scanf(" %c",&f1); to consume the newline character.
void Calculator()
{
int n,j;
char f1;
double t;
printf("please enter two numbers");
scanf("%d%d",&n,&j);
printf("please enter the syboml you want ( * / + or -)");
scanf(" %c",&f1);
if( f1 == '+')
t = n + j;
if (f1 == '-')
t = n-j;
if (f1 == '*')
t = n*j;
if (f1 == '/')
t = n/j;
printf("%f" ,t);
}
Not every program path leads to the assignment of the t variable. So it can be used in the printf not initialized.
switch(f1)
{
case '+':
t = n + j;
break;
case '-':
break;
t = n-j;
case '*':
t = n*j;
break;
case '/':
t = n/j;
break;
default:
t = 0;
break;
}
Now t will always will be assigned with the value.
Some additional remarks:
Always check the return value of the scanf
Your main definition is invalid. If main does not take any parameters is has to be int main(void)

print an int and char with the same identifier in C

I'm trying to do a simple code but I'm having hard time while trying to make it work.
I want to get an int from stdin between 1 and 50.
if the number is 1 then to print A
if the number is 11 then to print J
if the number is 12 then to print Q
if the number is 13 then to print K
Edit: If its none of them, then just return the number.
i tried to use %c but it wont work for numbers from 10 and above
then i managed to do it by using switch but the default part is not working for me. the only thing i managed to do is to make 50 case's for each number but that's just look horrible.
any help would be appreciated.
#include <stdio.h>
main() {
int number;
scanf("%d", &number);
char* card = NULL;
switch (number)
{
case 1:
card = "A";
break;
case 11:
card = "J";
break;
case 12:
card = "Q";
break;
case 13:
card = "K";
break;
default:
card = //Dont know what to write here//;
}
printf("%s\n", card);
return 0;
}
As far as I can see, this code should work:
#include <stdio.h>
#include <string.h>
#include <limits.h>
#define MAX_STRING_SIZE (sizeof(int)*CHAR_BIT/3 + 3)
int main(int argc, char **argv) {
int number = 0;
scanf("%d", &number);
char card[MAX_STRING_SIZE] = {0};
switch (number)
{
case 1:
strcpy(card, "A");
break;
case 11:
strcpy(card, "J");
break;
case 12:
strcpy(card, "Q");
break;
case 13:
strcpy(card, "K");
break;
default:
sprintf(card, "%d", number);
}
printf("%s\n", card);
return 0;
}
Note that you cannot assign strings with: card = "whatever", you must use strcpy() found in the string.h library. For the default part of your code, I assumed you were trying to convert the card input which was not 1, 11, 12, or 13. In which case, you can use sprintf() to convert the integer value to a string.
I hope this helps!
#define MINCARD 1
#define MAXCARD 50
const char *num2card(int n)
{
static char buf[3];
assert(n >= MINCARD && n <= MAXCARD && "Inval card no.");
switch (num) {
case 1:
return "A";
case 11:
return "J";
case 12:
return "Q";
case 13:
return "K";
default:
snprintf(buf, 3, "%d", num);
}
return buf;
}
Use an array of char. Each numerical "case" will correspond to an index in the array, and the element at that index will be the appropriate "card".
As for what the "default" value should be - that is a requirements question. What is the purpose of this application?
you can try sprintf, but you still have to add in logic for what happens if the number is larger than 50.
#include <stdio.h>
#include <stdlib.h>
int main() {
int number;
scanf("%d", &number);
char* card = malloc(8);
switch (number)
{
case 1:
card = "A";
break;
case 11:
card = "J";
break;
case 12:
card = "Q";
break;
case 13:
card = "K";
break;
default:
sprintf (card , "%i" , number );
}
printf("%s\n", card);
return 0;
}

Switch statements that read integers from input and reacts accordingly

I have a little task given to me which I am absolutely stumped on what to do.
The official brief is:
"Using switch, create a program that reads an integer from the keyboard and,
indicate that the number is smaller than 1 and or smaller than 10, and or
smaller than 100, and or smaller than 1000."
I've tried starting by typing "int num = scanf("%d\n", &num);"
and then doing the cases like follows
"case (num < 1 && <100): {
printf("Excellent!!\n" );
}"
But I am having no luck. Please can someone point me in the right direction.
Thanks,
EDIT:
Experimented with this but don't have any idea how I would get it to print Excellent:
#include <stdio.h>
int main () {
int num;
scanf("%d\n", &num);
switch(num) {
case 1:
{
if(num < 1 && num < 10) {
printf("Excellent!!\n" );
}
}
}
}
int num = scanf("%d\n", &num);
This line is wrong. scanf doesn't return the number it just read; it returns the number of elements read successfully. So change it to
scanf("%d\n", &num);
As for the switch, the valid form is
switch (constant-integral-expression) {
case one-label:
actions
break;
case another-label:
actions;
break;
default:
actions if none of the above were satisfied
}
where the default: clause is optional. For example, if you want to count spaces, newlines, and tabs:
int c;
int nspace, nnl, ntab;
for (nspace = nnl = ntab = 0; (c = getchar()) != EOF; ) {
switch (c) {
case ' ':
++nspace;
break;
case '\n':
++nnl;
break;
case '\t':
++ntab;
break;
}
}
printf("%d %d %d\n", nspace, nnl, ntab);
The actual program to write, is left as an exercise to the reader.
Edit: The break statement is crucial. Without the break statements at the end of each case, control would fall through to the next case. That is, the case statements following the first case would also be executed, and that can have undesired effects.
#include <stdio.h>
int main(void){
int num;
int range = 0;
printf("input num:\n");
scanf("%d", &num);
if(num < 1)
range = -1;
else {
while(num /= 10){
++range;
}
}
switch(range){
case -1:
puts("smaller than 1");
break;
case 0:
puts("smaller than 10");
break;
case 1:
puts("smaller than 100");
break;
case 2:
puts("smaller than 1000");
break;
default:
puts("More than 1000\n");
}
return 0;
}
You can do it something like the following
#include <stdio.h>
int main( void )
{
enum { LESS_THAN_1, LESS_THAN_10, LESS_THAN_100, LESS_THAN_1000 };
int a[] = { 1, 10, 100, 1000 };
int x;
printf( "Enter a number: " );
if ( scanf( "%d", &x ) == 1 )
{
int i = 0;
while ( i < sizeof( a ) / sizeof( *a ) && !( x < a[i] ) ) ++i;
switch ( i )
{
case LESS_THAN_1:
printf( "x is smaller than %d\n", a[i] );
break;
case LESS_THAN_10:
printf( "x is smaller than %d\n", a[i] );
break;
case LESS_THAN_100:
printf( "x is smaller than %d\n", a[i] );
break;
case LESS_THAN_1000:
printf( "x is smaller than %d\n", a[i] );
break;
default:
printf( "x is greater than or equal to %d\n", a[i-1] );
break;
}
}
}

Switch statement letter to integer

Assume that grades from A to F correspond to numbers from 1 to 6. Write a program that has three letter grades as inputs and prints the average numerical grade. My code looks like this : PS: I want this to print out the average of three letter inputs. (I take into account this code could be completely wrong, thus the final code should contain the "switch" and the calculation of the three corresponding values in numbers/interger. please help).
int main(){
char x,y,z;
int num;
float avg;
printf("\n Give three grades:\n");
scanf("%d %d %d", &x, &y, &z);
switch(x,y,z){
case 'a': return 1;
break;
case 'b': return 2;
break;
case 'c': return 3;
break;
case 'd': return 4;
break;
case 'e': return 5;
break;
case 'f': return 6;
break;
}
avg = x+y+z /3;
printf("\n The average is: %d \n", avg);
return 0;
}`
You can only switch on one value at a time. Why don't you make it a function:
int grade_value( char grade )
{
switch(grade) {
case 'a':
case 'A':
return 1;
case 'b':
case 'B':
return 2;
// etc...
default:
return 0;
}
}
Then just call it for each of x, y, and z. You really don't need to use a switch at all, but nevermind. eg
int grade_value( char grade )
{
int value = tolower(grade+1) - 'a';
return (value >= 1 && value <= 6) ? value : 0;
}
Also, note that you should use %f to output a float, not %d (which outputs an int).
You've also misunderstood how switch works. In your main, you switched and return from each case. That would exit main and return a value. That means your program would terminate.
You can use return in the function I suggested for you, because it's not your main. It will immediately return the given value from the function, thus you don't need `break'.

Resources