C C while-schleife bricht nicht ab (Anfängerproblem)

Stannis

Lieutenant
Registriert
Juli 2011
Beiträge
549
Tut mir leid, dass Ich mit so einer Kleinigkeit hier reinpoltere, aber Ich habe gerade niemanden sonst an der Hand.

Ich programmiere gerade sehr rudimentär nach Handbuch, Aufgabe ist es, einen Getränkeautomaten zu programmieren.

Mein Code (noch nicht fertig, nur Getränk 1 imo anwählbar, das ist aber nicht das Problem):
Code:
#include <stdio.h>

int main(){
	
	int g=0;
	printf("Servus. Wählen Sie Ihr Getränk:\n1) Wasser (0,50€)\n2) Cola (1,00€)\n3) Limo (1,50€)\n");
	scanf("%d", &g);	// Getränkewahl

	int anzahl;		// gewünschte Menge
	float geld, einwurf;	// geld = zu zahlende Summe, einwurf = repräsentiert Münzeinwurf

	if(g=1){
		printf("Anzahl?\n");
		scanf("%d", &anzahl);	// Mengenwahl
		geld = anzahl*0.5;
		printf("Werfen Sie %.2f € ein\n", geld);

		while(geld > 0){
			printf("Es fehlen noch %.2f €\n", geld);
			scanf("%f", &einwurf);
			geld=geld-einwurf;	// Restgeld-Berechnung
		}
		printf("Danke. Entnehmen Sie ihr viskoses Eigentum\n");
	}		
	
}

Das Problem: Manchmal - nicht immer, sondern scheinbar nur bei einer bestimmten Abfolge eingeworfener "Münzen" (repräsentiert durch die Eingabe von z.B. 0.2 für 0.20€) gibt mir das Programm aus, dass noch 0€ zu zahlen sind, und verlässt die Schleife erst, wenn Ich eigentlich zu viel gezahlt habe.

Shell:
Code:
Servus. Wählen Sie Ihr Getränk:
1) Wasser (0,50€)
2) Cola (1,00€)
3) Zitronenlimonade (1,50€)
1
Anzahl?
4
Werfen Sie 2.00 € ein
Es fehlen noch 2.00 €
1
Es fehlen noch 1.00 €
0.5
Es fehlen noch 0.50 €
0.2
Es fehlen noch 0.30 €
0.1
Es fehlen noch 0.20 €
0.2
Es fehlen noch 0.00 €
0
Es fehlen noch 0.00 €
0.1
Danke. Entnehmen Sie ihr viskoses Eigentum

Ich habe wirklich überlegt, aber Ich versteh's nicht. Ich habe in die Schleifenbedingung doch ganz ausdrücklich ">" und nicht ">=" geschrieben :freak:
 
Hallo,

die Logik selber stimmt soweit. Allerdings sobald du mit floats und doubles arbeitest, hast du eine gewisse Ungenauigkeit. D.h. wenn du die Variable "geld" auf 0 überprüfst, dann kann es sein, dass diese 0,000000002 ist und somit größer als 0. Auch wenn du von geld=0,2 weitere 0,2 abziehst. Dann kommt eben nicht genau 0 raus.

Lösung: while(geld - epsilon > 0){, wobei epsilon eine entsprechend kleine Zahl ist.

Hoffe es hilft dir weiter.

Viele Grüße
Seekuh
 
Ui, das klingt einleuchtend.
Da sieht man mal, worauf man alles achten muss.
Ich versuche es mal mit einem epsilon / Korrekturterm, dann muss ich mich nicht in die math.h einarbeiten *faulheit*

Ich danke euch.
 
Schnitzelsemmel schrieb:
Das Problem: Manchmal - nicht immer, sondern scheinbar nur bei einer bestimmten Abfolge eingeworfener "Münzen" (repräsentiert durch die Eingabe von z.B. 0.2 für 0.20€) gibt mir das Programm aus, dass noch 0€ zu zahlen sind, und verlässt die Schleife erst, wenn Ich eigentlich zu viel gezahlt habe.

Du rechnest immer und nur in ganzen Cent. Eine Eingabe von 0.2 wird dann: 20
 
Hallo,

gib doch einfach mal alle Stellen aus, dann wirst du die Ungenauigkeit sehen.

Wie schon vorher erwähnt entweder auf 2 Nachkommastellen runden

oder

in deinem Fall könnte man auch mit Integer rechnen und nicht Euro sondern Cent als Recheneinheit nehmen. Und nur für die Ausgabe auf Euro umrechnen.

Je nachdem welche C Version du verwendest bzw. in C# würde es noch den Decimal Datentyp geben, der genau rechnet.
 
Ist sehr lustig, wenn du z.B. eine while Schelife baust und auf einen Float immer 1 addierst kommen tlws. so Zahlen wie 2,99999999 raus
Das stimmt so nicht. Ganzzahlarithmetik funktioniert bei FP-Zahlen ohne Rundungsfehler sofern die Länge der Mantisse bei den vorhandenen Exponenten ausreicht.


@ TE
Eine anschauliche Begründung für die Ungenauigkeiten:
Die Ungenauigkeiten kommen daher, dass die Basis des binärsystems nur den Primfaktor 2 enthält, während die Basis des Dezimalsystems die Primfaktoren 2 und 5 enthält.

Deshalb kann man im Dezimalsystem nur diejenigen Brüche als Kommazahlen exakt darstellen, deren Nenner sich als ( 2^n * 5^m) schreiben lässt. Analogerweise sind im Binärsystem nur diejenigen Brüche als Kommazahlen exakt darstellbar, deren Nenner sich als (2^n) schreiben lässt. Somit lassen sich im Dezimalsystem mehr Brüche exakt darstellen als im Binärsystem.

Beispiel: Wenn du nun 0.05 im Dezimalsystem eingibst so ist das als Bruch 1/25 bzw 1/5^2. Ergo nicht exakt im Binärsystem darstellbar. Wenn du im Dezimalsystem aber 0.125 eingibst so ist das als Bruch 1/8 bzw 1/2^3. Das lässt sich exakt im Binärsystem als 0.001 darstellen.
 
Ich versuche es mal mit einem epsilon / Korrekturterm, dann muss ich mich nicht in die math.h einarbeiten *faulheit*
Warum machst du es dir nicht einfach und stellst den Geldbetrag als Integer dar? :p
Wie das sinnvoll geht, überlasse ich mal deiner Fantasie, jemand hat das aber bereits angedeutet.

Floats haben ihre Daseinsberechtigung und ihre Einsatzgebiete, dieses hier ist aber keines davon. Du benötigst hier ja eine exakte Darstellung des Geldbetrags.

Nebenbei sind viele verkettete Additionen und besonders (!) Subtraktionen so ziemlich das Schlimmste, was man machen kann, wenn sich die Zahlen nicht exakt darstellen lassen. Nehmen wir mal folgendes Programm:

Code:
#include <stdio.h>

int divide(float n, float q) {
  return n < q ? 0 : 1 + divide(n - q, q);
}

int main(int argc, char** argv) {
  const float n = 10000.0f;
  const float q = 0.01f;
  printf("Ergebnis:           %d\n", divide(n, q));
  printf("Korrektes Ergebnis: %d\n", (int)(n / q));
}

Was kommt heraus?
Code:
Ergebnis:           1013801
Korrektes Ergebnis: 1000000
Das entspricht recht genau dem, was dein Programm aktuell macht, wenn du 10000€ mit 1 Cent-Stücken bezahlen willst. Das ist schon ein recht großer Fehler, der da entsteht.

Nebenbei ist natürlich auch das "korrekte" Ergebnis je nach Wahl der Zahlen mit Vorsicht zu genießen, aber Multiplikationen und Divisionen reagieren generell nicht so stark auf Fehler.

Nai schrieb:
[...] anschaulich ... Primfaktoren [...] (2^n*5^m) [...]
:freak: :lol:
 
Zuletzt bearbeitet:
VikingGe schrieb:
:heul:

Vielleicht noch eine Anmerkung zu meinem Vorherigen Post: Das ist auch der Grund dafür, dass moderne Taschenrechner eigentlich in der Regel im Dezimalsystem und nicht, so wie es als Irrglaube weit verbreitet ist, im Binärsystem rechnen.
 
Zuletzt bearbeitet:
Nai schrieb:
:heul:

Vielleicht noch eine Anmerkung zu meinem Vorherigen Post: Das ist auch der Grund dafür, dass moderne Taschenrechner eigentlich in der Regel im Dezimalsystem und nicht, so wie es als Irrglaube weit verbreitet ist, im Binärsystem rechnen.

Wie meinen? Die Schaltkreise im Taschenrechner arbeiten doch selbstverständlich binär (Potentiale high und low), oder?

thecain schrieb:
if (g=1) tut wahrscheinlich auch nicht das was du erwartest...
gnah. Das erklärt, wieso Ich nur Wasser kaufen kann. Das ist zwar gesund, aber hier nicht Sinn der Sache.
Miuwa schrieb:
Typo?

@Topic: Do NEVER use floating point for currency!

Für was nutzt man dann float und double überhaupt?

Und wo wir schon dabei sind: Wofür brauche Ich dauernd diese main-funktion?
 
Für was nutzt man dann float und double überhaupt?
Allgemein meist für wissenschaftliche Berechnungen, wissenschaftliche Simulationen oder spezieller in der Computergraphik, Robotik, KI, Physik, Ingenieurszeugs, uvm.

Wie meinen? Die Schaltkreise im Taschenrechner arbeiten doch selbstverständlich binär (Potentiale high und low), oder?
Auf der untersten Ebene ja. Aber darüber wird dann ein Dezimalsystem aufgebaut. Ist zwar nicht sehr effizient, aber bei der einen Berechnung die ein Taschenrechner so in der Sekunde schaffen muss, spielen Performance und Effizienz keine Rolle.

Und wo wir schon dabei sind: Wofür brauche Ich dauernd diese main-funktion?
Wie meinen? Das ist per Definition die Einstiegsfunktion. Dein Compiler sucht die Main-Funktion im Quelltext und sorgt dafür, dass im Maschinen-Code der Main-Thread am Beginn der Main-Funktion anfängt zu arbeiten.
 
Zuletzt bearbeitet:
Das Stichwort beim Taschenrechner ist BCD: Binary Coded Decimal. Man bildet je 4 Bit eine Dezimalziffer ab (und keine Hexadezimale, wie eigentlich möglich).

float und double nutzt man dann wenn man tatsächlich mit reelllen Zahlen in ihrem vollem Umfang Arbeiten will oder muss (und dabei die entsprechende Ungenauigkeit kein Problem darstellt). Als Beispiel mal physikalische Berechnungen oder auch 3D Computergrafik.

Und main() ist der definierte Einstiegspunkt in dein Programm. Ist einfach festgelegte Konvention dass sie so heißen muss, damit der Compiler/Linker weiß wo das Programm starten soll.
 
Aber manche Dinge kann man doch auch außerhalb der Main-funktion schreiben, wenn Ich das richtig verstanden habe?
 
Befehle, die das Programm später ausführen soll, müssen immer in einer Funktion verpackt sein. Außerhalb von Funktionen darf das Programm nur Definitionen, Deklarierungen oder statische(?) Initialisierungen beinhalten (hoffe das passt so).
 
Zuletzt bearbeitet:
Schnitzelsemmel schrieb:
Aber manche Dinge kann man doch auch außerhalb der Main-funktion schreiben, wenn Ich das richtig verstanden habe?

Die main()- Funktion ist etwas für den C-Programmierer Heiliges, es ist der Punkt, an welchem die Laufzeitumgebung des Betriebssystems Dein Programm anspringt:

"The main function serves a special purpose in C programs; the run-time environment calls the main function to begin program execution. "​

Andere Funktionen, also "Programmblöcke" ausserhalb von main(), rufst Du selber auf.
 
Zuletzt bearbeitet:
Zurück
Oben