Programm spuckt falsche Zahlen aus



  • Hallo,

    als Anfänger kann ich das Problem nur deskriptiv beschreiben:

    Es handelt sich um ein Statistikprogramm, dass zur Zeit einen T-Test berechnen soll (später mehr).

    Hier die Funktion zur Berechnung auf Grundlage einer in einer anderen Funktion ausgelesenen Textdatei, welche den Datensatz temporär gespeichert hat (Projektname, Gruppenanzahl, Gruppenname, Gruppengröße, Subject-Werte).

    Ab Zeile 45 spuckt er falsche Werte aus.
    http://imgur.com/qlHB1dB

    void fTtest()
    {
    	string nGroupName[50];																// LOADING VARIABLES
    																						// ***
    	string nProjectName = fTempLoad(0);	
    
    	int nGroupCount = stoi(fTempLoad(1))+1;
    
    	for (int iii=0; iii<nGroupCount; iii++)
    		nGroupName[iii] = fTempLoad(iii+2);
    
    	int * nGroupSize = new int (nGroupCount);											// 
    	for (int iii=0; iii<=nGroupCount; iii++)
    		nGroupSize[iii] = stoi(fTempLoad(iii+2+nGroupCount));
    
    	double ** nValue = new double*[nGroupCount];	
    	for (int kkk = 0; kkk < nGroupCount; kkk++)											// Creating multidimensional [2], dynamic Array
    	{																					// 
    		nValue[kkk] = new double[1000];
    	}
    
    	int nRead = 2+nGroupCount*2;	
    	for (int iii=0; iii<nGroupCount; iii++)
    	{		
    		for (int jjj=0; jjj<nGroupSize[iii]; jjj++)
    		{				
    			nValue[iii][jjj] = stod(fTempLoad(nRead));									// Gathering values
    			nRead++;																	
    		}												
    	}						
    
    	double * nMean = new double(nGroupCount);
    	for (int iii=0; iii<nGroupCount; iii++)
    	{		
    		for (int jjj=0; jjj<nGroupSize[iii]; jjj++)						
    			nMean[iii] = nValue[iii][jjj];																			
    	}				
    
    	for (int iii=0; iii<nGroupCount; iii++) {		
    		cout << "Mean: " << nMean[iii] << " / " << nGroupSize[iii] << " = ";
    		nMean[iii] = nMean[iii] / nGroupSize[iii];
    		cout << nMean[iii] << endl;
    	}	
    
    	double * nVariance = new double(nGroupCount);
    	for (int iii=0; iii<nGroupCount; iii++) {
    		for (int jjj=0; jjj<nGroupSize[iii]; jjj++) 
    			{
    			nVariance[iii] = (nValue[iii][jjj] - nMean[iii]) * (nValue[iii][jjj] - nMean[iii]); 
    			cout << "Var: (" << nValue[iii][jjj] << " - " << nMean[iii] << ")2 = " << nVariance[iii] << endl;
    			}
    	}
    
    	for (int iii = 0; iii < nGroupCount; iii++)								// Delete dynamical Arrays
    		delete [] nValue[iii]; 
        delete [] nValue; 
    
    	delete [] nMean;
    	delete [] nVariance;
    	delete [] nGroupSize;
    
    system ("PAUSE");
    }
    

    In einer anderen Funktion wurde die Eingabe bereits durch den Benutzer in eine .txt Datei gespeichert, um sie dann wieder auszulesen (die Datei wird beim Neustart immer wieder gelöscht, es handelt sich also um eine temporäre Datei). Dieser Teil funktioniert einwandfrei, aber vielleicht ist er dennoch für die Problemdiagnostik interessant...
    Hier die Funktion:

    void fNewData()
    {
    				system("cls");												// CASE: T-TEST
    				Navigator eNav;
    				fHeadBody(eNav = NEW_DATA);
    
    	string nProjectName = fProjectName();											// PROJECT NAME (FUNCTION)	
    
    				system("cls");
    				fHeadBody(eNav = NEW_DATA);
    
    	cout << "Project: " << nProjectName << endl;
    	cout << "\nEnter group\'s names - end with \"ok\".\n" << endl;
    
    	string nGroupName[50];
    	int iii = -1;	
    	bool nEnd = false;
    	do 
    	{	
    		iii++;	
    		nGroupName[iii] = fGroupName(iii);									// GROUP NAME & GROUP COUNT
    		if (nGroupName[iii] == "ok")										// end with "ok"
    		{
    			nEnd = true;
    			iii--;
    		}
    	} while (!nEnd);
    
    	int nGroupCount = iii;													// gets nGroupCount
    
    	double ** nValue = new double*[nGroupCount];	
    	for (int kkk = 0; kkk <= nGroupCount; kkk++)							// Creating multidimensional [2], dynamic Array
    	{																		
    		nValue[kkk] = new double[1000];
    	}
    
    	int * nValueCount = new int[nGroupCount];								// Count Values/Subjects within Groups, dynamical Array allocation
    																			// WARNING !!! NO CLEAN UP DECLARED YET !!!
    	for (int jjj = 0; jjj <= nGroupCount; jjj++)
    	{	
    				system("cls");
    				fHeadBody(eNav = NEW_DATA);
    
    		cout << "Project: " << nProjectName << " with";
    		for (int kkk = 0; kkk <= nGroupCount; kkk++)
    		{
    			cout << " [" << nGroupName[kkk] << "]";
    		}
    		cout << endl;
    		cout << "> Enter subject values for " << nGroupName[jjj] << " (type \"ok\" to end)\n" << endl;
    
    		iii = 0;	
    		bool nBreak = false;												// Fetch subject variables Group A, break if "ok"	
    		do
    		{				
    			string nStop;
    			cout << "(" << nGroupName[jjj] << ") - Value " << iii+1 << ": ";
    			cin >> nValue[jjj][iii];
    			iii++;
    			if (cin.fail())
    			{
    				cin.clear();
    				cin >> nStop;												// (buffer is ready and continues in >> nStop, until it's empty)
    				if (nStop != "ok")
    					cout << "\n\tERROR: Invalid value!\n\tOnly (floating) numbers." << endl;
    				iii--;								
    				if (nStop == "ok")
    				{
    					nBreak = true;
    					nValueCount[jjj] = iii;									
    				}
    			}	
    		} while (!nBreak);
    	}
    
    				system("cls");
    				fHeadBody(eNav = NEW_DATA);									// SAVING DATA (OR TEMP-SAVE)
    				cout << "Allocation done!\n" << endl;
    				cout << "Project:\t" << nProjectName << endl;
    				for (int iii = 0; iii <= nGroupCount; iii++)
    					cout << "Group " << iii+1 << ":\t" << nGroupName[iii] << "\t\t(" << nValueCount[iii] << " subjects)" << endl;
    				cout << "\nSave your data?\n[Y]es / [N]o > ";
    				while (true)
    				{
    					char nAnswer = _getch();
    					if (nAnswer == 'Y' || nAnswer == 'y')
    					{	
    						while (true)
    						{
    							//fSaveData(index) --> TBD!
    							fSaveData(nProjectName);						// Save Order:	(1) Index (TBD)		
    							ostringstream nStr;								//				(2) Project Name	
    							nStr << nGroupCount;							//				(3)	Group Count	(begins with 0 (2 Groups = 1))
    							string nString(nStr.str());						//				(4) Group Name(s) [i = Group Count]
    							fSaveData(nString);								//				(5) Group Size(s) [i = Group Count]
    							for (int iii=0; iii<=nGroupCount; iii++)		//				(6) Values [i = Group Count, j = Group Size]
    								fSaveData(nGroupName[iii]);
    							for (int iii=0; iii<=nGroupCount; iii++)
    							{
    								ostringstream nStr;							
    								nStr << nValueCount[iii];
    								string nString(nStr.str());
    								fSaveData(nString);
    							}
    							break;
    						}
    						for (int iii = 0; iii <= nGroupCount; iii++)
    						{
    							for (int jjj = 0; jjj < nValueCount[iii]; jjj++)
    							{
    								double nSave = nValue[iii][jjj];			// Double to String via <sstream>
    								ostringstream nStr;												 		
    								nStr << nSave;
    								string nString(nStr.str());
    								cout << "Saving... " << nString;
    
    								int nOK = fSaveData(nString);				// Call function fSaveData to save						
    								if (nOK == 0)								// Option to include Error Messages
    									cout << "OK!" << endl;
    								else if (nOK == 1)
    								{
    									cout << "ERROR while saving " << nString << endl;
    									break;
    								}
    							}
    						}					
    					break;					
    					}					
    					else if (nAnswer == 'N' || nAnswer == 'n')
    					{
    						cout << "\nData not saved." << endl;
    						Sleep (500);
    						while (true)
    						{
    							//fTempData(index) --> TBD!
    							fTempData(nProjectName);						// Save Order:	(1) Index (TBD)		
    							ostringstream nStr;								//				(2) Project Name	
    							nStr << nGroupCount;							//				(3)	Group Count	(begins with 0 (2 Groups = 1))
    							string nString(nStr.str());						//				(4) Group Name(s) [i = Group Count]
    							fTempData(nString);								//				(5) Group Size(s) [i = Group Count]
    							for (int iii=0; iii<=nGroupCount; iii++)		//				(6) Values [i = Group Count, j = Group Size]
    								fTempData(nGroupName[iii]);
    							for (int iii=0; iii<=nGroupCount; iii++)
    							{
    								ostringstream nStr;							
    								nStr << nValueCount[iii];
    								string nString(nStr.str());
    								fTempData(nString);
    							}
    							break;
    						}
    						for (int iii = 0; iii <= nGroupCount; iii++)
    						{
    							for (int jjj = 0; jjj < nValueCount[iii]; jjj++)
    							{
    								double nSave = nValue[iii][jjj];			// Double to String via <sstream>
    								ostringstream nStr;												 		
    								nStr << nSave;
    								string nString(nStr.str());								
    								int nOK = fTempData(nString);				// Call function fTempData to temporary save						
    								if (nOK == 0)								// Option to include Error Messages
    									cout << ".";
    								else if (nOK == 1)
    								{
    									cout << "ERROR while buffering at " << nString << endl;
    									break;
    								}
    							}
    						}
    						break;
    					}
    				}
    	for (int iii = 0; iii < nGroupCount; iii++)								// Delete dynamical Arrays
    		delete [] nValue[iii]; 
        delete [] nValue; 
    
    	delete [] nValueCount;
    }
    

    Mein Problem: Wenn ich in der Eingabe 3 Gruppen eingebe, funktioniert alles wunderbar. Gebe ich allerdings nur 2 Gruppen und ihre Werte ein, berechnet das Programm diese falsch, bzw. in Zeile ## steht zwar der richtige Mean-Wert, aber im cout steht dann eine Zahl wie -1.823723e2, als würde er irgendetwas falsches auslesen. Wenn ich 4 Gruppen eingegeben habe, stürzt er noch vor der Ausgabe der einzelnen Werte ab.

    Hier findet Ihr einen Screenshot. Der Output dient zur Problemfindung...
    http://imgur.com/qlHB1dB

    Ich weiß beim besten Willen nicht, was da schief läuft. Ich vermute ja, es liegt an den dynamischen Arrays, aber da ich noch blutiger Anfänger bin, weiß ich nicht, was genau das Problem ist, und wie es zu lösen ist.

    Ich wäre sehr dankbar um Hilfe!



  • Eieiei, du hast da einige gravierende Fehler.

    int * nGroupSize = new int (nGroupCount);
    

    Was du hier wohl meinst (basierend auf den folgenden Zeilen), ist eher

    int* nGroupSize = new int[nGroupCount];
    

    Das wäre technisch korrekt, aber das ist trotzdem nicht der richtige Weg. Schau dir mal std::vector an und ersetze alle dynamischen arrays durch vector. Bei mehrdimensionalen Arrays will man meistens einen vector von arrays, hier am besten std::array (c++11) nutzen.

    Du solltest die Wahl deiner Bezeichner überdenken. f scheint bei dir für Funktionen zu stehen, das n habe ich noch nicht so ganz begriffen. Davon abgesehen ist diese sogenannte ungarische Notation bestenfalls umstritten, es wird eher davon abgeraten. Ich mags auch nicht.
    Und ehrlich: ein Buchstabe für einen Schleifenindex reicht. Warum denn iii? Sieht nicht so schön aus. Und warum ändern sich die Bezeichner deiner Schleifen? Mal i(ii), mal k(kk).

    Du durchläufst mehrere Schleifen, die von 0 bis nGroupCount gehen. Warum? Mach doch eine Schleife draus. Dadurch wird auch der Code viel kürzer und übersichtlicher.

    Das sind erstmal so Sachen, die auffallen, so richtig durchgelesen habe ich deinen Code aber nicht. Das wichtigste: rück deinen Code ordentlich ein, an vielen Stellen ist das schon gut, an einigen nicht so. Vergiss operator new, den brauchst du als Anfänger nie. Niemals.

    Was erwartest du die hiervon? (Zeile 88 - 106, zweites Listing)

    while (true)
    {
        // ...
        break;
    }
    

    Wenn du das umgesetzt hast, kann man immer noch über dein Problem reden. Wenn du alles vernünftig umgesetzt wird und du bei der Berechnung keine semantischen Fehler hast, wird wahrscheinlich dann alles prima sein.

    EDIT: mal vom anderen abgesehen: wie lernst du? Wie kommst du auf so ein exzessives Nutzen von new? Das ist übel. Kauf dir ein gutes Buch, wie z.B. etwas von Breymann oder den C++ Primer.



  • Ich habe die Lösung nach vielen Stunden gefunden...

    1. erst einmal durch deine Hilfe, denn ich habe [] tatsächlich mit () "verwechselt" - vielen Dank! Wäre mir nicht aufgefallen...
    2. zweitens wurde ich Dank des Debuggers auf den Heap aufmerksam, den ich wohl "überzogen" habe (???)... jedenfalls beginnt nGroupCount mit dem Wert 0, und ist daher immer 1 weniger als ich dachte (ich habe drei Gruppen, nGroupCount ist aber 2). Und mit nGroupCount habe ich via new[] die dynamischen Arrays erstellt, die dadurch immer eine Speicherzuweisung weniger hatten, als sie sein sollten. Das Problem ließ sich mit new[nGroupCount+1] lösen.

    Ja, meine Bezeichner sind zur Zeit fFunktion, nVariable, pPointer, etc. ... ich habe das so für mich persönlich festgelegt, damit ich anfangs etwas den Überblick behalte (z. B. bei neuen/ungewohnten Funktionen, was davon nun die Variable ist). Teilweise ist es Ungarische Notation, teilweise mein "Style".

    Die "while(true)" Schleife sollte nur dazu dienen, dass sie endlos läuft, bis sie durch eine korrekte Eingabe abgebrochen wird. Habe das auf diese Art und Weise gelöst.

    Ich lerne mit diesem Tutorial (und bin bisher sehr zufrieden damit):
    http://www.learncpp.com/

    Das Statistikprogramm, an dem ich arbeite, soll für mich persönlich als Wiederholung der dort durchgearbeiteten Themen dienen (bevor ich in Kapitel 8 zur objektorientierten Programmierung komme). Alles mögliche, was ich gelernt habe, soll darin vorkommen. Dynamische Arrays wurden hier mit Pointer und new[] gelöst, weshalb ich das auch in meinem Programm umsetzen wollte. Mit Vektoren habe ich bisher nicht gearbeitet, werde ich aber (falls es im o.g. Tutorial nicht erwähnt wird), separat für mich durcharbeiten.

    Nochmals besten Dank!



  • Katharsis schrieb:

    Ich lerne mit diesem Tutorial (und bin bisher sehr zufrieden damit):
    http://www.learncpp.com/

    Ein Tutorial oder Buch, das dir new/delete vor vector beibringt, ist definitiv nicht gut.



  • Noch eine kleine Frage, ohne ein extra Thema aufmachen zu wollen...

    Mein Programm läuft bisher ohne Probleme - keine Abstürze, keine falschen Werte, alles wie gewollt...

    trotz allem habe ich (unter Visual C++ 2010 Express) den Debugger laufen lassen, der also so mein Programm abklappert.
    Dabei stoppt er ca 6 mal, und ich habe die Möglichkeit, ihn zu unterbrechen und das Problem anzugehen, oder weiter laufen zu lassen.

    Er meldet sich wegen Dinge, die mir nicht so ganz klar sind. Hier die Meldungen:

    Unbehandelte Ausnahme bei 0x7550c42d in StatCalc.exe: Microsoft C++-Ausnahme: std::invalid_argument an Speicherposition 0x0031eccc..

    und

    Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

    Es handelt sich wohl um diese C++ Zeilen:

    nGroupSize[iii] = stoi(fTempLoad(iii+2+nGroupCount));
    
    int nGroupCount = stoi(fTempLoad(1))+1;
    
    int nSelectTest = fSelectTest();
    

    Dazu zeigt er mir separate Files an:
    xthrow.cpp
    string
    crtexe.c
    mit deren Inhalt (in C) ich nichts anfangen kann.

    Meine Frage(n) hierzu:
    - wenn das Programm an sich ohne Probleme läuft und compiliert wird, sind solche (und im Speziellen: die oben genannten) Fehlermeldungen des Debuggers zu ignorieren bzw. haben die (dann noch) Relevanz?
    - und falls ja, welche Probleme hat der Debugger mit o.g. Zeilen?

    Sorry für die sehr allgemeinen Fragen...
    Danke



  • "Unbehandelte Ausnahme" heisst dass eine Exception geworfen wurde, die nirgends gefangen wurde. Das ist komisch, denn das dürfte das Programm eigentlich nicht überleben.

    "The value of ESP was not properly saved across a function call." ist auch ganz übel, das heisst ... naja, das was da steht halt (ESP ist der Stack-Pointer). Normalerweise bedeutet es dass man sich den Stack irgendwie zerschossen hat.

    Konkret zu deinen Fragen: ja, diese Fehler sind immer von Relevanz. Es ist auch seltsam dass dein Programm damit überhaupt läuft. Speziell der erste Fehler müsste zu einem Programmabbruch führen.

    Und der Debugger hat damit gar kein Problem, der meldet dir bloss dass dein Programm ein Problem hat.
    Was genau da schief geht lässt sich aus der Ferne schwer sagen. Speziell was es mit der Exception auf sich hat lässt sich mit dem Debugger aber ganz leicht feststellen: stell den einfach so ein dass er anhalten soll sobald irgendwo irgend eine Exception geworfen wird. Dann siehst du wo, und auch warum std::invalid_argument geworfen wird.

    Die "ESP" Sache ist schwieriger. Aber halte das Programm einfach mal an der Stelle an, wo der Debugger das Problem meldet, und guck dir an was das Programm zu dem Zeitpunkt macht. Oft genug ist der Fehler in der "unmittelbaren Umgebung" zu finden.



  • Das Anhalten des Debuggers habe ich bereits getan, ich sehe (aus meiner Sicht) keine Fehler. Dabei weiß ich nicht mal, was Exceptions sind, habe die also kaum selbst erstellt (oder?).

    Allerdings arbeite ich viel mit fstream, und wenn der Debugger läuft (oder das Programm "aus dem IDE heraus" gestartet wird), speichert/liest er nicht aus den Textdateien (sondern eben nur, wenn ich die Release *.exe starte). Könnte daher der Fehler kommen? Quasi, dass er aus einem nicht vorhandenen string versucht, diesen in einen integer umzuwandeln?

    Denn er hält ja bei diesem Code an:

    int nGroupCount = stoi(fTempLoad(1))+1;
    

    und die Funktion "fTempLoad()" liest aus dem fStream, was aber im Debugging Modus (und aus dem "öffnen nach compilen"-Modus) eben nicht funktioniert, sondern nur im Release .exe

    string nProjectName = fTempLoad();
    --> kein Problem (da String=String, und lt. Debugger ist der Wert "")

    int nGroupCount = stoi(fTempLoad());
    --> Fehler (leerer String -> Int, lt. Debugger ist der Wert 16777216, also irgendein Matsch)

    Ist das schlüssig?



  • std::stoi
    http://en.cppreference.com/w/cpp/string/basic_string/stol

    std::invalid_argument if no conversion could be performed

    Daher könnte z.B. die Exception kommen.

    Woher der "ESP" Fehler kommt kann ich so ohne Weiteres nicht sagen.
    Vielleicht weil fGroupName nie "ok" zurückliefert, daher die Schleife nie abgebrochen wird, daher iii >= 50 wird und daher die Bounds des Arrays nGroupName überschritten werden? Und du dir dadurch den Stack zerschiesst.
    Irgendwas in dieser Art vermutlich.

    Du musst auf jeden Fall sicherstellen dass dein Programm, egal was für Input es bekommt, keinen Mist baut. Wenn Mist als Input daherkommt, dann musst du halt irgendwie kontrolliert abbrechen.
    Einfach Daten einlesen bis irgend eine Markierung erkannt wird, und dann hoffen dass es nicht mehr als 50 Einträge werden, ist halt keine gute Lösung.

    Katharsis schrieb:

    Allerdings arbeite ich viel mit fstream, und wenn der Debugger läuft (oder das Programm "aus dem IDE heraus" gestartet wird), speichert/liest er nicht aus den Textdateien (sondern eben nur, wenn ich die Release *.exe starte). Könnte daher der Fehler kommen? Quasi, dass er aus einem nicht vorhandenen string versucht, diesen in einen integer umzuwandeln?

    Ja klar kann daher der Fehler kommen, siehe oben.
    Erstmal solltest du sicherstellen dass auch in so einem Fall dein Programm nicht einfach abkackt.

    Und dann solltest du das Programm so anpassen dass es auch im Debug-Mode das richtige Textfile aufmacht. z.B. indem du einen absoluten Pfad verwendest, das Working-Directory im Debugger passend einstellst oder einfach das Textfile dort hinkopierst wo das Programm es erwartet wenn es auf dem Debugger gestartet wird.

    Macht ja schliesslich wenig Sinn wenn du das Programm im Debugger (nur) unter ganz anderen Bedingungen testest als die unter denen es dann laufen soll.


Anmelden zum Antworten