Taschenrechner Programm - Mehrere Probleme



  • Hallo!

    Vorab: Ich nutze DevC++ mit MinGW

    Folgende Aufgabe muss ich Lösen:

    Programmieren Sie einen einfachen Taschenrechner. Ihr Taschenrechner soll nach dem Start ein Menü anzeigen, das sich nach folgendem Schema orientiert:
    (+) Addition
    (-) Subtraktion
    ...

    Nach der Wahl einer Operation (das Zeichen in Klammern muss eingegeben werden), fragt der Taschenrechner die beiden Operanden ab. Anschließend gibt er Term und Ergebnis der jeweiligen Operation auf der Textkonsole aus. Danach soll erneut das Menü angezeigt werden. Das Programm wird erst durch Wahl von "x" beendet.

    Wichtig: Der Taschenrechner soll ausschließlich ganze Zahlen verwenden und unterstützen. Überprüfen Sie daher vor der Berechnung, ob unter Verwendung der gegebenen Operation und Operanden ein Überlauf/Unterlauf eintritt und geben Sie in einem solchen Fall einen Warnhinweis aus.

    Ich hab natürlich schon einen Lösungsentwurf, allerdings hat der noch einige Krankheiten, die ich ihm einfach nicht austreiben kann. Hier erst einmal die Fragen und darunter der komplette Code:

    1. Beim Kompilieren bringt er immer die Fehlermeldung:
      [Error] 'ergebnis' undeclared (first use in this function)
      Aber ich hab doch die Variable "int ergebnis" in der menue() deklariert. Diese Variable wird doch dann an alle darunter liegenden Blöcke weitergegeben und die Funktionen Addition(), etc. müssten darauf zugreifen können, oder hab ich da jetzt nen Denkfehler drin 😕
      Wenn ich die Variable global deklariere, funktionierts.

    2. Gibt es ausser return; eine Möglichkeit das Programm komplett zu schließen? Also auch die Konsole, in der es ausgeführt wird?

    3. In der Angabe steht, ich soll vor der Berechnung prüfen, ob Overflow/Underflow eintritt. Wie kann ich das machen? Ich glaubm, dass meine Überprüfung in der Funktion check_overflow_underflow nämlich sinnlos ist, da das ergebnis dann ja schon berechnet ist und der evtl. overflow/underflow schon stattgefunden hat und sich die Variable ergebnis ja wieder im richtigen Wertebereich (wenn auch mit falschem Ergebnis) befindet.

    4. Bei der Prüfung, ob der Operator korrekt eingeben wurde, krieg ich immer die Fehlermeldung "ERROR: Operator ist ungueltig!", auch wenn ich einen korrekten Operator eingegeben habe! Warum? Was stimmt da nicht?

    #include <stdio.h>
    #include <limits.h>
    
    //Funktionsprototypen
    void menue();
    int check_overflow_underflow (int ergebnis);    //Gibt 1 bei Overflow/Underflow zurück und 0 wenn alles OK
    void Addition (int a, int b);
    void Subtraktion (int a, int b);
    void Multiplikation (int a, int b);
    void Division (int a, int b);
    
    int main (int argc, char *argv[])
    {
    	menue();
    
    	return 0;
    }
    
    void menue (){
    
    	char operator;
    	int operand_1;
    	int operand_2;
    	int ergebnis;
    
    	//Überschrift
    	printf("Taschenrechnermenue:\n");
    	printf("Es werden nur ganzzahlige Operanden unterstuetzt!\n\n");
    
    	//Auswahlmenü Operationen
    	printf("Folgende Operationen stehen zur Verfuegung:\n");
    	printf("Addition (+)\n");
    	printf("Subtraktion (-)\n");
    	printf("Multiplikation (*)\n");
    	printf("Division (/)\n");
    	printf("\nZum Beenden des Taschenrechners Geben Sie bei Operator (x) ein!!!");
    
    	//Eingaben der Auswahl inkl. Operanden
    	printf("\n\nBitte waehlen Sie einen Operator: ");
    	fflush(stdin);
    	scanf("%c",&operator);
    
    	if (operator == 'x')
    	{
    		return;
    	}
    
    	//Prüfen, ob ein gültiger Operator-Typ angegeben wurde!
    	if (operator != '+' || operator != '-' || operator != '*' || operator != '/')
    	{
    		printf("\n\nERROR: Operator ist ungueltig!\n\n");
    		menue();
    	}
    
    	printf("\nGeben Sie Operand 1 ein: ");
    	fflush(stdin);
    	scanf("%d",&operand_1);
    
    	printf("\nGeben Sie Operand 2 ein: ");
    	fflush(stdin);
    	scanf("%d",&operand_2);
    
    	//Prüfung, ob auch wirklich eine int-Zahl eingegeben wurde fehlt noch!
    
    	//Auswahl der Operation
    	switch (operator){
    
    		case '+':
    			Addition(operand_1, operand_2);
    			menue();
    
    		case '-':
    			Subtraktion(operand_1, operand_2);
    			menue();
    
    		case '*':
    			Multiplikation(operand_1, operand_2);
    			menue();
    
    		case '/':
    			Division(operand_1, operand_2);
    			menue();
    	}
    
    }
    
    int check_overflow_underflow (ergebnis){
    
    	if (ergebnis >= INT_MAX)
    	{
    		printf("ERROR: Integer Overflow!");
    		return 1;
    	}
    	else if (ergebnis <= INT_MIN)
    	{
    		printf("ERROR: Integer Underflow!");
    		return 1;
    	}
    	else
    	{
    		return 0;
    	}
    
    }
    
    void Addition (int a, int b){
    
    	ergebnis = a + b;
    
    	if (check_overflow_underflow (ergebnis) == 0)
    	{
    		printf("\n\n%d + %d = %d\n\n",a,b,ergebnis);
    	}
    
    }
    
    void Subtraktion (int a, int b){
    
    	ergebnis = a - b;
    
    	if (check_overflow_underflow (ergebnis) == 0)
    	{
    		printf("\n\n%d - %d = %d\n\n",a,b,ergebnis);
    	}
    
    }
    
    void Multiplikation (int a, int b){
    
    	ergebnis = a * b;
    
    	if (check_overflow_underflow (ergebnis) == 0)
    	{
    		printf("\n\n%d * %d = %d\n\n",a,b,ergebnis);
    	}
    
    }
    
    void Division (int a, int b){
    
    	if (b == 0)
    	{
    		printf("ERROR: Es darf nicht durch 0 geteilt werden!");
    		return;
    	}
    
    	ergebnis = a / b;
    
    	if (check_overflow_underflow (ergebnis) == 0)
    	{
    		printf("\n\n%d / %d = %d\n\n",a,b,ergebnis);
    	}
    
    }
    


  • Was mir noch eingefallen ist:

    Wäre eine

    do {
    
    }while (operator == 'x');
    

    sinnvollere Alternative? Oder ist das so, wie ich es implementiert hab, auch ok?



  • Zu 1)
    Da liegst du voll daneben. Die Funktionen sind in sich abgeschlossen, da kann keine andere Funktion reinschauen und Variablen nutzen.
    Sobald eine Variable zwischen {} definiert ist, ist sie ausserhalb nicht mehr sichtbar.
    Du solltest den Rückgabewert der Funktionen nutzen und die Ausgabe ausserhalb machen.

    Zu 2)
    Ja, exit
    Aber das brauchst du nicht.

    Zu 3)
    Du kannst nur mit dem Ergebnis nicht erkennen, ob es ein Überlauf/Unterlauf ist.
    Dazu brauchst du auch die Operanden und es ist für jede Rechenoperation anders. (Kann es bei / einen Überlauf/Unterlauf geben?
    Z.B muss bei einer Addition von Positiven Zahlen, die Summe größer sein als jeder Summand.

    Zu 4)
    Dann überleg nochmal was raus kommt, wenn du einen Operator hast:
    `operator = '+';

    if (operator != '+' || operator != '-' || operator != '*' || operator != '/')`
    Dann hast du unwahr || wahr || wahr || wahr Und das ist wahr.
    Überleg dir nochmal die Verknüpfung.

    Was soll das "//Prüfung, ob auch wirklich eine int-Zahl eingegeben wurde fehlt noch!" bringen, da du scanf nur Ganzzahlen einlesen läßt.

    marcm244 schrieb:

    Wäre eine

    do {
    
    }while (operator == 'x');
    

    sinnvollere Alternative? Oder ist das so, wie ich es implementiert hab, auch ok?

    Das ist das einzig sinnvolle.
    Im Augenblick rufst du menue rekursiv auf. Darum hast du auch Probleme mit dem return. -> Nicht OK.



  • Also das mit der Prüfung auf Überlauf oder Unterlauf hab ich jetzt mal vorerst weggelassen. Dann kann ich erst die anderen Probleme lösen und mich danach darum kümmern.
    Ich hab aber mal die Lösung eines Komilitonen (der allerdings schon mehr Ahnung von der Sache hat) unter meinen Quellcode gesetzt. Der hat auch das mit der Prüfung auf Überlauf/Unterlauf drin (ob's richtig ist, weiß ich nicht). Allerdings kapier ich seine Vorgehensweise nicht wirklich. Bei den Funktionen

    BOOL CheckOverflow(char op, int operand1, int operand2);
    BOOL operationIsSafe(int a, int b);
    size_t log2(int a);
    

    setzt bei mir aus...

    Wieder zurück zu meinem Code:

    Hab den Code jetzt nochmal aufgrund deiner Hinweise verändert. Jetzt läuft er und tut (soweit ich das getestet hab) alles, was er soll. Hab auch das do...while implementiert. Das war eigentlich auch mein erster Gedanke, aber keine Ahnung, warum ich den dann verworfen hab...

    Die Prüfung des Operanden hab ich jetzt auch geändert!
    Ich wollte eigentlich den aktuellen Schleifendurchlauf mit continue abbrechen, wenn ein Fehler auftritt (so dass er den "nächsten" Schleifendurchlauf beginnt), aber das hat er nicht gemacht, deswegen habe ich die main() Funktion dann einfach wieder aufgerufen. Gibts dafür ne bessere Alternative oder passt das so?
    "break" beendet die Schleife ja komplett, damit wäre mir ja nicht geholfen.

    #include <stdio.h>
    #include <limits.h>
    
    //Funktionsprototypen
    int Addition (int a, int b);
    int Subtraktion (int a, int b);
    int Multiplikation (int a, int b);
    int Division (int a, int b);
    
    int main (){
    
    	char operator;
    	int operand_1;
    	int operand_2;
    
    	do{
    
    		//Überschrift
    		printf("Taschenrechnermenue:\n");
    		printf("Es werden nur ganzzahlige Operanden unterstuetzt!\n\n");
    
    		//Auswahlmenü Operationen
    		printf("Folgende Operationen stehen zur Verfuegung:\n");
    		printf("Addition (+)\n");
    		printf("Subtraktion (-)\n");
    		printf("Multiplikation (*)\n");
    		printf("Division (/)\n");
    		printf("Zum Beenden des Taschenrechners Geben Sie bei Operator (x) ein!!!");
    
    		//Eingabe des Operators
    		printf("\n\nBitte waehlen Sie einen Operator: ");
    		fflush(stdin);
    		scanf("%c",&operator);
    
    		//Prüfen auf "Programm beenden"
    		if (operator == 'x')
    		{
    			return 0;
    		}
    
    		//Prüfen, ob ein gültiger Operator-Typ angegeben wurde!
    		if ( !(operator == '+' || operator == '-' || operator == '*' || operator == '/') )
    		{
    			printf("\n\nERROR: Operator ist ungueltig!\n\n");
    			main();
    		}
    
    		//Einlesen der Operanden
    		printf("\nGeben Sie Operand 1 ein: ");
    		fflush(stdin);
    		scanf("%d",&operand_1);
    
    		printf("\nGeben Sie Operand 2 ein: ");
    		fflush(stdin);
    		scanf("%d",&operand_2);
    
    		//Auswahl der Operation
    		switch (operator)
    		{
    			case '+':
    				printf("\n\n%d + %d = %d\n\n",operand_1, operand_2, Addition(operand_1, operand_2));
    				main();
    
    			case '-':
    				printf("\n\n%d - %d = %d\n\n",operand_1, operand_2, Subtraktion(operand_1, operand_2));
    				main();
    
    			case '*':
    				printf("\n\n%d * %d = %d\n\n",operand_1, operand_2, Multiplikation(operand_1, operand_2));
    				main();
    
    			case '/':
    				printf("\n\n%d / %d = %d\n\n",operand_1, operand_2, Division(operand_1, operand_2));
    				main();
    		}
    
    	} while (operator == 'x');
    
    	return 0;
    }
    
    int Addition (int a, int b){
    
    	return a + b;
    
    }
    
    int Subtraktion (int a, int b){
    
    	return a - b;
    
    }
    
    int Multiplikation (int a, int b){
    
    	return a * b;
    
    }
    
    int Division (int a, int b){
    
    	if (b == 0)
    	{
    		printf("ERROR: Es darf nicht durch 0 geteilt werden!");
    		return;
    	}
    
    	return a / b;
    
    }
    

    **
    Code des Komilitonen:**

    // Calculator.cpp : Defines the entry point for the console application.
    // Ian Staples feat Roland Seda
    // Keine perfekte Lösung!
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    typedef int BOOL;
    void RunCalculator();
    int Calculate(char op, int operand1, int operand2);
    BOOL CheckOverflow(char op, int operand1, int operand2);
    BOOL operationIsSafe(int a, int b);
    size_t log2(int a);
    void PrintHelp();
    
    int main()
    {
    	RunCalculator();
    
    	return EXIT_SUCCESS;
    }
    
    void RunCalculator()
    {
    	char op;
    	int operand1, operand2;
    
    	for(;;)
    	{
    		printf("Operator> ");
    		fflush(stdin);
    		scanf("%c", &op);
    
    		if (op == 'x')
    		{
    			printf("Byebye!");
    			break;
    		}
    		else if (op == 'h')
    		{
    			PrintHelp();
    			continue;
    		}
    
    		printf("Zahl 1> ");
    		fflush(stdin);
    		scanf("%d", &operand1);
    
    		printf("Zahl 2> ");
    		fflush(stdin);
    		scanf("%d", &operand2);
    
    		if (!CheckOverflow(op, operand1, operand2))
    			printf("Rechnung koennte overflow verursachen und somit das Ergebniss verfaelschen!\n");
    
    		printf("\t -> %d\n", Calculate(op, operand1, operand2));
    	}
    }
    
    int Calculate(char op, int operand1, int operand2)
    {
    	int result;
    
    	switch (op)
    	{
    	case '+':
    		result  = operand1 + operand2;
    		break;
    	case '-':
    		result  = operand1 - operand2;
    		break;
    	case '*':
    		result  = operand1 * operand2;
    		break;
    	case '/':
    		result  = operand1 / operand2;
    		break;
    	case '%':
    		result  = operand1 % operand2;
    		break;
    	}
    
    	return result;
    }
    
    BOOL CheckOverflow(char op, int operand1, int operand2)
    {
    	switch (op)
    	{
    	case '*':
    	case '+':
    	case '-':
    		return operationIsSafe(operand1, operand2);
    	default:
    		return 1;
    	}
    }
    
    BOOL operationIsSafe(int a, int b)
    
    {
    	size_t a_bits = log2(a), b_bits = log2(b);
    
    	return a_bits + b_bits <= 31;
    }
    
    size_t log2(int a)
    {
    	size_t bits = 0;
    
    	while (a != 0 && a != -1)
    	{
    		++bits;
    		a >>= 1;
    	}
    
    	return bits;
    }
    
    void PrintHelp()
    {
    	printf(" (*) Multiplikation \n (/) Division \n (+) Addition \n (-) Subtraktion \n (x) Exit \n (h) Hilfe \n");
    }
    


  • marcm244 schrieb:

    Ich wollte eigentlich den aktuellen Schleifendurchlauf mit continue abbrechen, wenn ein Fehler auftritt (so dass er den "nächsten" Schleifendurchlauf beginnt), aber das hat er nicht gemacht, deswegen habe ich die main() Funktion dann einfach wieder aufgerufen. Gibts dafür ne bessere Alternative oder passt das so?

    Jetzt machst du alles in main statt in menue. Auch den rekursiven Aufruf. Wo ist da der Unterschied zu vorher? Das ist Sch.... *, Mist 🙄

    Die do-while-Schleife wird ausgeführt, solange die Bedingung wahr ist.
    Mach die Bedingung richtig, dann klappt es auch mit dem continue.
    Eine Alternative wäre, die Berechnungen in einen else-Zweig (Zeile 50 -78 )zu packen.

    Die Testfunktionen von deinem Komilitonen sind nett, aber auch falsch.
    Er sucht jeweils das höchste gesetzte Bit und addiert die beiden Werte.

    Allerdings ist die Summe von zwei 16-Bit Zahlen maximal eine 17-Bit Zahl.
    INT_MAX + INT_MIN gibt bei ihm einen Überlauf.

    Habt ihr den Schrott mit dem fflush(stdin); von eurem Tutor? 👎 😮

    *rott



  • Also ich hab das jetzt mit dem "else" Zweig realisiert. Finde, das ist die eleganteste Lösung.

    Den rekursiven Aufruf hab ich auch raus und die Ausgabe hab ich in die main() gelegt.

    Ja, das mit dem fflush(stdin) stand als Hinweis auf dem Arbeitsblatt. Wenn ich das allerdings nicht vor scanf() schreibe, dann fragt er mich beim zweiten Aufrufdurchgang nicht mehr nach dem Operator. Das war nämlich das erste große Problem, dass ich hatte. Dann hab ich einfach mal fflush(stdin) vor alle scanf() davor gepfeffert und siehe da, es hat funktioniert. Hab dann gleich gegoogelt, was fflush() macht.

    Prüfung:

    Jetzt fehlt eigentlich nur noch die Prüfung, wann ein Über-/Unterlauf stattfindet. Muss ich das jetzt für jede Funktion (Addition(), etc) einzelln programmieren?

    Angabe der Aufgabe:
    "WICHTIG: Der Taschenrechner soll ausschließlich ganze Zahlen verwenden und unterstützen. Überprüfen Sie daher vor der Berechnung, ob unter Verwendung der gegebenen Operation und Operanden ein Überlauf/Unterlauf eintritt und geben Sie in einem solchen Fall einen Warnhinweis aus."

    Das Ergebnis muss halt zwischen - 2147483648 und +2147483647 liegen, sonst tritt ein Über-/Unterlauf ein, richtig?
    Nur wenn ich das schon errechnete Ergebnis dann prüfe, dann kann es ja sein, dass z.b. die Berechnung einen Überlauf produziert hat und ich somit bspw. -5 rausbekomme. Das läge ja wieder im Wertebereich von int. Somit ist die Prüfung des Ergebnisses ja sinnlos.
    Vielleicht hat jemand nen kleinen Denkanstoss?

    Hier nochmal der aktuelle (funktionierende) Code:

    /*
    	Programm funktioniert - Prüfung fehlt!
    */
    
    #include <stdio.h>
    #include <limits.h>
    
    //Funktionsprototypen
    int Addition (int a, int b);
    int Subtraktion (int a, int b);
    int Multiplikation (int a, int b);
    int Division (int a, int b);
    
    int main (){
    
    	//beseitigt einige Probleme mit Ausgabe, Debugging etc.
    	setbuf(stdout, NULL);
    
    	char operator;
    	int operand_1;
    	int operand_2;
    
    	do{
    
    		//Überschrift
    		printf("Taschenrechnermenue:\n");
    		printf("Es werden nur ganzzahlige Operanden unterstuetzt!\n\n");
    
    		//Auswahlmenü Operationen
    		printf("Folgende Operationen stehen zur Verfuegung:\n");
    		printf("Addition (+)\n");
    		printf("Subtraktion (-)\n");
    		printf("Multiplikation (*)\n");
    		printf("Division (/)\n");
    		printf("Zum Beenden des Taschenrechners Geben Sie bei Operator (x) ein!!!");
    
    		//Eingabe des Operators
    		printf("\n\nBitte waehlen Sie einen Operator: ");
    		fflush(stdin);
    		scanf("%c",&operator);
    
    		//Prüfe auf Operator 'x', um Programm zu beenden
    		if (operator == 'x')
    		{
    			break;
    		}
    
    		//Prüfen, ob ein gültiger Operator-Typ angegeben wurde!
    		if ( !(operator == '+' || operator == '-' || operator == '*' || operator == '/') )
    		{
    			printf("\n\nERROR: Operator ist ungueltig!\n\n");
    			break;
    		}
    		else
    		{
    			//Einlesen der Operanden
    			printf("\nGeben Sie Operand 1 ein: ");
    			fflush(stdin);
    			scanf("%d",&operand_1);
    
    			printf("\nGeben Sie Operand 2 ein: ");
    			fflush(stdin);
    			scanf("%d",&operand_2);
    
    			//Auswahl der Operation
    			switch (operator)
    			{
    				case '+':
    					printf("\n\n%d + %d = %d\n\n",operand_1, operand_2, Addition(operand_1, operand_2));
    					continue;
    
    				case '-':
    					printf("\n\n%d - %d = %d\n\n",operand_1, operand_2, Subtraktion(operand_1, operand_2));
    					continue;
    
    				case '*':
    					printf("\n\n%d * %d = %d\n\n",operand_1, operand_2, Multiplikation(operand_1, operand_2));
    					continue;
    
    				case '/':
    					printf("\n\n%d / %d = %d\n\n",operand_1, operand_2, Division(operand_1, operand_2));
    					continue;
    			}
    		}
    	} while (1);
    
    	return 0;
    }
    
    int Addition (int a, int b){
    
    	return a + b;
    
    }
    
    int Subtraktion (int a, int b){
    
    	return a - b;
    
    }
    
    int Multiplikation (int a, int b){
    
    	return a * b;
    
    }
    
    int Division (int a, int b){
    
    	if (b == 0)
    	{
    		printf("ERROR: Es darf nicht durch 0 geteilt werden!");
    		return;
    	}
    
    	return a / b;
    
    }
    


  • Zu deinem Über/Unterlauf:
    Überleg dir mal was zu einem Über- oder Unterlauf führen kann.

    DirkB schrieb:

    Z.B muss bei einer Addition von positiven Zahlen, die Summe größer sein als jeder Summand.

    Das kannst du auch für die Subtraktion anpassen.
    Es ist bei signed int nicht vom Standard geregelt, was bei einem Überlauf passiert.
    Spiel mal etwas mit den Werten rum. Was passiert, wenn du INT_MAX-Summand1 rechnest. Muss die Differenz größer/gleich/kleiner Summand2 sein?

    Zu dem scanf("%c",..) und dem fflush(stdin) :
    Nimm nur scanf(" %c",..) und achte auf das Leerzeichen vor dem %( kein fflush(stdin) )

    Willst du bei dem

    printf("\n\nERROR: Operator ist ungueltig!\n\n");
                break;
    

    wirklich das Programm beenden?
    Oder statt des break ein continue nehmen.



  • Ok, ich hab mir für den Überlauf folgendes überlegt:

    Addition:
    (Summe > Summand1) && (Summe > Summand 2)

    Subtraktion:
    So einfach es wahrscheinlich auch ist... ich glaub ich hab hier nen Hänger... ich komm einfach nicht auf ne allgemeine Formulierung....

    Multiplikation:
    a und b positiv
    Summe > [(Summand1 - 1) * (Summand2 - 1)]

    Division:
    Hier kann kein Überlauf/Unterlauf stattfinden.

    Stimmt das so, oder hab ich irgendwo nen Logik-Fehler drin ?!

    Das mit dem Leerzeichen vor %c bei scanf() hab ich probiert. Funktioniert!
    Kannst du mir erklären, was das Leerzeichen da macht? Oder hast vielleicht nen Link zu ner Erklärung?

    Danke für den Hinweis. Es sollte natürlich continue heißen 🙂 .



  • marcm244 schrieb:

    Das mit dem Leerzeichen vor %c bei scanf() hab ich probiert. Funktioniert!
    Kannst du mir erklären, was das Leerzeichen da macht? Oder hast vielleicht nen Link zu ner Erklärung?

    Guckst du hier: http://www.cplusplus.com/reference/cstdio/scanf/ unter Whitespace character

    Ein Leerzeichen (oder jeder andere Whitespace character) veranlasst scanf dazu eben diese zu überlesen.
    Bei dir ist noch das '\n' von der Enter-Taste von der Eingabe von operand_2 im Eingabestrom.

    scanf überliest bei den meisten Formatspecifiern die Whitespace. Aber nicht bei %c. Es könnte ja sein, dass du gerade das '\n' einlesen möchtest.


Anmelden zum Antworten