Problem mit Ergebnisabgleich



  • Hallo Community,
    ich hab ein Problem mit meinem "Taschenrechner-Spiel".

    Es funktioniert bei der Schwierigkeitsstufe 1(case 1) einwandfrei, aber sobald ich die Schwierigkeitsstufe 3(case 3) wähle, wird mir ab und zu das richtige Ergebnis als Falsch angezeigt. Wisst ihr woran das liegen könnte? Ich komme nicht drauf.

    #include <stdlib.h>
    #include <iostream>
    #include <time.h>
    using namespace std;	
    
    int a;
    double b,c,x,y,z;
    int Addition()
    	{
    		int F=0,R=0;
    	switch(a)
    		{
    	case 1:
    			for (int i=1; i<=10; i++)
    			{
    		int Zahl1=rand()%100;
    		int Zahl2=rand()%100;
    		int e=Zahl1+Zahl2;
    		cout<<i<<". Aufgabe: Geben Sie das Ergebnis ein: "<<endl;
    		cout<<Zahl1<<"+"<<Zahl2<<"=";
    		cin>>b;
    		if (b == e)
    			{R++;
    		cout<<"Richtig ;)"<<endl;system("PAUSE");}
    		else 
    		{
    			F++;
    			cout<<"Falsch :(\n"<<endl;
    			cout<<"Die Loesung waere: "<<e<<endl;
    			system("PAUSE");
    		};
    		system("cls");
    		cout<<"Naechste Aufgabe:"<<endl;
    		};
    			system("cls");
    			cout<<"Sie haben "<<R<<" Aufgaben richtig und "<<F<<" Aufgaben falsch beantwortet\n\n"<<endl;
    			break;
    	case 2:
    			for (int i=1; i<=10; i++)
    			{
    				double Zahl1=rand()%100/(double)10;
    				double Zahl2=rand()%100/(double)10;
    				double e=Zahl1+Zahl2;
    				cout<<i<<". Aufgabe: Geben Sie das Ergebnis ein: "<<endl;
    				cout<<Zahl1<<"+"<<Zahl2<<"=";
    				cin>>b;
    			if (b == e)
    			{
    				R++;
    				cout<<"Richtig ;)"<<endl;system("PAUSE");
    			}
    			else 
    			{
    				F++;
    				cout<<"Falsch :(\n"<<endl;
    				cout<<"Die Loesung waere: "<<e<<endl;
    				system("PAUSE");
    			};
    		system("cls");
    		cout<<"Naechste Aufgabe:"<<endl;
    		};
    			system("cls");
    			cout<<"Sie haben "<<R<<" Aufgaben richtig und "<<F<<" Aufgaben falsch beantwortet\n\n"<<endl;
    		break;
    	case 3:
    			for (int i=1; i<=10; i++)
    			{
    			double Zahl1=rand()%1000/(double)10;
    			double Zahl2=rand()%1000/(double)10;
    			double e=Zahl1+Zahl2;
    			cout<<i<<". Aufgabe: Geben Sie das Ergebnis ein: "<<endl;
    		cout<<Zahl1<<"+"<<Zahl2<<"=";
    		cin>>b;
    		if (b == e)
    			{R++;
    		cout<<"Richtig ;)"<<endl;system("PAUSE");}
    		else 
    		{
    			F++;
    			cout<<"Falsch :(\n"<<endl;
    			cout<<"Die Loesung waere: "<<e<<endl<<endl;
    			system("PAUSE");
    		};
    		system("cls");
    		cout<<"Naechste Aufgabe:"<<endl;
    		};
    			system("cls");
    			cout<<"Sie haben "<<R<<" Aufgaben richtig und "<<F<<" Aufgaben falsch beantwortet\n\n"<<endl;
    			break;
    	};
    return 0;	
    };
    int main()
    {
    	srand (time(NULL));
    	cout<<" ____________________ "<<endl;
    	cout<<"|                    |"<<endl;
    	cout<<"|Taschenrechner Spiel|"<<endl;
    	cout<<"|____________________|"<<endl;
    	cout<<" *****"<<"Spielmenue"<<"*****\n"<<endl;
    	cout<<"Starten (1) \nBeenden (0)"<<endl;
    	cin>>x;
    	system("cls");
    	if (x==1)
    	{
    		do
    		{
    		cout<<"Es folgen 10 Aufgaben. Waehlen Sie den Schwierigkeitsgrad:"<<endl;
    		cout<<"Einfach (1)\nNormal  (2) \nSchwer  (3)\nBeenden(0)"<<endl;
    		cin>>a;
    		system("cls");
    		Addition();
    		}while(a!=0);
    	}
    	else
    		if (x==0)
    		{
    			cout<<"Das Programm wird beendet"<<endl;
    			system ("PAUSE");
    		}
    		else 
    		{
    			cout<<"Falsche Eingabe"<<endl;
    		};
    	cout<<"Das Programm wird beendet"<<endl;
    	system ("PAUSE");
    }
    

  • Mod

    Tritt das Problem nur bei nicht-ganzen Zahlen auf? Aber nicht bei Zahlen, deren Nachkommateil .5, .25, .75, .125, .375, .625, oder .875 ist? Falls ja, dann ist das Problem, dass Fließkommazahlen intern (fast immer) mit einem Binärformat arbeiten. Dann sind Zahlen, die keinem Bruch mit einer Zweierpotenz im Nenner entsprechen, nicht exakt darstellbar. Das ist ungefähr so, als würdest du nach dem Ergebnis von 1/3 + 1/3 fragen und müsstest dieses als Kommazahl exakt angeben. Wird ein bisschen schwierig...
    In einem Binärsystem sind bloß eben andere Zahlen exakt darstellbar als in einem Zehnersystem*.

    *: Tatsächlich sind alle Zahlen, die im Binärsystem mit endlich vielen Stellen exakt angegeben werden können, auch im Zehnersystem exakt darstellbar, da 2 ein Primfaktor von 10 ist. Bloß umgekehrt eben nicht. Hingegen bei einem Vergleich von z.B. Zehnersystem und Dreiersystem hat man überhaupt keine Überschneidungen (außer natürlich ganze Zahlen), da keine gemeinsamen Primfaktoren. Im Dreiersystem ist dann auch die obige Aufgabe 1/3 + 1/3 ganz leicht exakt zu beantworten, das ist nämlich 0.13 + 0.13 = 0.23.



  • okay, das würde ja heißen, dass das Programm mit Zahlen rechnet, die mehrere Nachkommastellen haben, aber es mir nur eine Nachkommastelle ausgibt. Infolge dessen rechne ich mit einer Nachkommastelle und komme natürlich nicht auf das exakte Ergebnis.

    Grade bekam ich diese Aufgabe von meinem Programm:

    66.4 + 5.4 =

    Als Ergebnis gab ich 71.8 an, was auch richtig wäre, aber es wird mir als falsch angezeigt, wobei das von dem Programm errechnete Ergebnis auch 71.8 ist.

    Wenn ich mir nun "case 3" betrachte, ist Zahl 1 eine Zufallszahl zwischen 0 und 1000 durch 10, sowie Zahl 2 auch. D.h. das ja aber nie Zahlen wie 99.95 raus kommen, sondern nur mit einer Nachkommastelle. Und daher sollte ja auch das Ergebnis richtig sein.

    Ich habe wahrscheinlich einfach ein Verständnisproblem 😃


  • Mod

    Inpakt schrieb:

    66.4 + 5.4 =

    Als Ergebnis gab ich 71.8 an, was auch richtig wäre, aber es wird mir als falsch angezeigt, wobei das von dem Programm errechnete Ergebnis auch 71.8 ist.

    Das ist genau der beschriebene Fall. Etwas, das auf .4 oder .8 endet, kann in einem Binärsystem niemals exakt dargestellt werden. Wenn du die Anzahl der angezeigten Stellen mal mittels precision auf 15 oder mehr erhöhst (Standard ist 7), dann wirst du sehen, dass die 66.4 in Wahrheit 66.4000000000000056843418860808 ist. Das ist der Wert, der 66.4 am nächsten kommt. Entsprechen ist 5.4 = 5.40000000000000035527136788005. Und 71.8 ist 71.7999999999999971578290569596. Und jetzt siehst du das Problem: Der Computer führt die Rechnung schon durchaus exakt durch. Aber die Zahlen, die am ehesten 66.4 und 5.4 entsprechen ergeben zusammen nicht die Zahl, die am ehesten 71.8 entspricht, sondern einen leicht anderen Wert.

    Wenn ich mir nun "case 3" betrachte, ist Zahl 1 eine Zufallszahl zwischen 0 und 1000 durch 10, sowie Zahl 2 auch. D.h. das ja aber nie Zahlen wie 99.95 raus kommen, sondern nur mit einer Nachkommastelle.

    Aber nur im Zehnersystem. Wie schon erklärt hat 1/3 im Dreiersytem auch nur eine Nachkommastelle, aber im Zehnersystem ist das bekanntlich 0.33333.....
    Was du machen könntest:
    a) Vergiss doubles. Rechnungen werden ja nicht schwerer dadurch dass ein Komma drin ist. Du lässt ja sowieso nur wenige Nachkommastellen zu. Ob man nun im Zahlenraum 0-100 mit einer Nachkommastelle rechnet oder im Zahlenraum 0-1000 kommt doch aufs gleiche raus.
    b) Führe den Vergleich mit einer gewissen Toleranz aus. Es reicht doch sicher, wenn der Benutzer auf 13 Stellen genau ist.
    c) Du kannst natürlich auch Zahlen generieren, die der Computer exakt darstellen kann. Du musst eben im Zweiersystem denken. Statt durch 10 teilst du deine Zahlen eben durch eine Zweierpotenz wie 8 oder 16, also zum Beispiel rand()%1000/(double)16;
    Das sollte funktionieren.

    Den Cast der 10 kannst du dir übrigens sparen, gib deine Zahlen doch gleich als double an, indem du einen Punkt anfügst:

    rand()%1000/(double)10;
    // zu
    rand()%1000/10.0;
    // oder auch ohne 0
    rand()%1000/10.;
    // (oder halt mit 8. oder 16., siehe c))
    


  • Fließkommazahlen (double, float) halte ich hier für komplett falsch - ist ja wie bei Währungsangaben (€, $, ...), dies sollte man auch als Cent (also als Ganzzahlen) speichern (und dann entsprechend zur Ausgabe formatieren).



  • Alles klar, ich danke euch.

    Hab es verstanden und den Fehler behoben. Jetzt funktioniert es 🙂


Anmelden zum Antworten