Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Geldautomat (https://www.delphipraxis.net/158778-geldautomat.html)

Matz4000 2. Mär 2011 16:34

Delphi-Version: 5

Geldautomat
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hey Leute,
Im Anhang findet ihr einen von mir programmierten kleinen "Geldautomat". (Hausaufgabe)
Man gibt einen Preis ein und einen Schein, mit dem man bezahlt.

Mit Hilfe von While-Do-Schleifen soll dann der "Automat" Kleingeld in einer Listbox ausgeben, mit möglichst wenig Münzen.
Dies klappt auch soweit, aber wenn man z.B. den Preis von 4,98€ eingibt (10-Euroschein) vergisst er einen cent.
Und bei 4,99 ebenfalls. Bei 4.97 stimmts allerdings wieder.
Woran könnte das liegen?

Hier mein Quelltext:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var S:integer;
    Preis:real;
    R:real;
begin
Preis:=strtofloat(edit1.text);
S:=strtoint(edit2.text);
listbox1.clear;
R:=S-Preis;
if (R<9.99) then
begin
 While R>=2
 Do begin
      listbox1.Items.add('2-Euro-Stück');
      R:=R-2;
    end;
 While R>=1
 Do begin
      listbox1.Items.add('1-Euro-Stück');
      R:=R-1;
    end;
 While R>=0.5
 Do begin
      listbox1.Items.add('50-cent-Stück');
      R:=R-0.5;
    end;
 While R>=0.2
 Do begin
      listbox1.Items.add('20-cent-Stück');
      R:=R-0.2;
    end;
 While R>=0.1
 Do begin
      listbox1.Items.add('10-cent-Stück');
      R:=R-0.1;
    end;
 While R>=0.05
 Do begin
      listbox1.Items.add('5-cent-Stück');
      R:=R-0.05;
    end;
 While R>=0.02
 Do begin
      listbox1.Items.add('2-cent-Stück');
      R:=R-0.02;
    end;
 While R>=0.01
 Do begin
      listbox1.Items.add('1-cent-Stück');
      R:=R-0.01;
    end;
end
           else application.messagebox('Der Automat gibt ausschließlich Kleingeld und nur bis 9,99&#8364;.','Stop!',mb_ok)






end;

end.

p.s. R = Rückgeld
S = Schein

s.h.a.r.k 2. Mär 2011 16:38

AW: Geldautomat
 
Bei 4,97 stimmt es, weil dabei keinerlei Ein-Cent-Stück ausbezahlt werden. War schon ein Wink mit dem Zaunpfahl, wo das Problem liegt, oder? ;)

--EDIT
Noch ein Tipp: Gleitkommazahlen sollte man nie mit "=" vergleichen (also z.B. [delphi]a = 0.01[/delphi)), hier steht warum. Daher mein Tipp: Rechene mit Cent-Beträgen, wodurch du auf Real verzichten und Integer (oder besser Cardinal) nutzen kannst. Integer bzw. Real haben nicht dieses "Ungenauigkeitsproblem" und somit kann man ohne Probleme mit "=" arbeiten.

Wenn du dennoch mit Real-Werten arbeiten willst, dann schau dir mal die CompareValue-Methoden an. Vielleicht musst du dazu die Math-Unit einbinden.

Deep-Sea 2. Mär 2011 16:45

AW: Geldautomat
 
Oder halt Currency als Datentyp nutzen?! :-D

PS: Man könnte die fast immer gleiche Schleife auch in eine Funktion auslagern ... macht's vlt. übersichtlicher.

himitsu 2. Mär 2011 16:47

AW: Geldautomat
 
Und nimm mal statt Delphi-Referenz durchsuchenReal lieber das Delphi-Referenz durchsuchenCurrency.
Bei Geld sollte man Rundungsprobleme besser ausschließen.

rollstuhlfahrer 2. Mär 2011 16:47

AW: Geldautomat
 
Und bei 4,96 stimmts auch? - Wenn nicht liegts an der Genauigkeit deiner Zahlen.

Bernhard

Matz4000 2. Mär 2011 18:05

AW: Geldautomat
 
Ok danke für die schnellen Antworten...
aber @s.h.a.r.k:
Ich meinte mit 4.97 den Preis d.h. das Rückgeld beträgt 5.03 und da wird ein cent ausgezahlt...

Es klappt wirklich alles wies klappen soll außer das er manchmal n cent verschluckt...
Ich glaube kaum das das was mit Real und Currency zutun hat...aber danke für den Hinweis ;)

Frage mich immer noch woran das hängen könnte...

Gruß


Edit:
Wie ich sehe hat noch niemand das Programm sich angeschaut, ich würds empfelen vll. kommt ihr dann drauf...

BUG 2. Mär 2011 18:09

AW: Geldautomat
 
Zitat:

Zitat von Matz4000 (Beitrag 1085444)
Ich glaube kaum das das was mit Real und Currency zutun hat...aber danke für den Hinweis ;)

Hast du es ausprobiert? :mrgreen:

EDIT: Wenn du das gemacht hast, können wir evtl. nochmal über Genauigkeit diskutieren.

Matz4000 2. Mär 2011 18:22

AW: Geldautomat
 
Ok ich muss mich entschuldigen :D
Ihr hattet recht...unglaublich das das DARAN jetzt was geändert hat...ich versteh allerdings immer noch nicht warum ?!

gruß

BUG 2. Mär 2011 18:40

AW: Geldautomat
 
Einige Zahlen die im Dezimalsystem ohne nicht periodisch sind, sind periodisch in der Binärdarstellung (genau wie 1/3 = 0,3 im Dezimalsystem):

0,1d = 0,0000110b

Da man aber nur einen bestimmten Platz hat, kann man nicht alle Stellen speichern.
Deshalb gibt es Ungenauigkeiten. Mehr auf Wikipedia ...

Currency vermeidet diese Ungenauigkeit, indem es die Nachkommastellen auf 4 begrenzt und damit rechnet als wären es Ganzzahlen.

Matz4000 2. Mär 2011 18:49

AW: Geldautomat
 
ahh ok danke ;)

Deep-Sea 3. Mär 2011 08:29

AW: Geldautomat
 
Um genau zu sein ist Currency eine Festkommazahl. In Delphi intern ist es ein normaler 64-bit Integer (bei dem die vier niederwertigsten Stellen als Nachkommastellen angesehen werden).

Und wie gesagt: Die Schleifen bieten sich geradezu an, in eine Funktion ausgelagert zu werden. Und auch die Strings wie "2-Euro-Stück" könnte man automatisch erzeugen. :-D

Jumpy 3. Mär 2011 10:48

AW: Geldautomat
 
Kann man nicht das ganze mit nur einer Schleife lösen?
Delphi-Quellcode:
while R<> 0 do
  begin
    if R>2 then
      begin
        listbox.Add ...;
        R=R-2;
      end
    else if R>1 then
      begin
        listbox.Add ...;
        R=R-1;
      end
    else if ...

  end
Mach den Code jetzt auch nicht schöner, aber man hat nur eine Schleife...
Oder hab ich da jetzt einen Denkfehler drin?

jfheins 3. Mär 2011 10:54

AW: Geldautomat
 
Zitat:

Zitat von Jumpy (Beitrag 1085538)
Kann man nicht das ganze mit nur einer Schleife lösen?´
Mach den Code jetzt auch nicht schöner, aber man hat nur eine Schleife...
Oder hab ich da jetzt einen Denkfehler drin?

Ja, geht - macht den Code aber weder schöner noch schneller von daher isses egal ;)

Deep-Sea 3. Mär 2011 10:57

AW: Geldautomat
 
@Jumpy:
Mh, sollte gehen. Aber was hat man davon? Es sind immer noch zig Redundanzen drin. Darum sagte ich ja auch auslagern in eine Funktion ^^

DeddyH 3. Mär 2011 11:24

AW: Geldautomat
 
Evtl. so?
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
//aus Bequemlichkeitsgründen Konstanten definiert statt Eingaben auszuwerten
const
  Preis  = 5.01;
  Einwurf = 10;
var
  Rest: Currency;
begin
  ListBox1.Items.BeginUpdate;
  try
    Rest := Einwurf - Preis;
    Rueckgabe(trunc(Rest * 100));
  finally
    ListBox1.Items.EndUpdate;
  end;
end;

procedure TForm1.Rueckgabe(WertInCent: integer);
begin
  if WertInCent > 0 then
    case WertInCent of
      1:
        begin
          ListBox1.Items.Add('1 Cent');
          Rueckgabe(WertInCent - 1);
        end;
      2 .. 4:
        begin
          ListBox1.Items.Add('2 Cent');
          Rueckgabe(WertInCent - 2);
        end;
      5 .. 9:
        begin
          ListBox1.Items.Add('5 Cent');
          Rueckgabe(WertInCent - 5);
        end;
      10 .. 19:
        begin
          ListBox1.Items.Add('10 Cent');
          Rueckgabe(WertInCent - 10);
        end;
      20 .. 49:
        begin
          ListBox1.Items.Add('20 Cent');
          Rueckgabe(WertInCent - 20);
        end;
      50 .. 99:
        begin
          ListBox1.Items.Add('50 Cent');
          Rueckgabe(WertInCent - 50);
        end;
      100 .. 199:
        begin
          ListBox1.Items.Add('1 Euro');
          Rueckgabe(WertInCent - 100);
        end;
    else
      begin
        ListBox1.Items.Add('2 Euro');
        Rueckgabe(WertInCent - 200);
      end;
    end;
end;

Deep-Sea 3. Mär 2011 12:36

AW: Geldautomat
 
Zitat:

Zitat von DeddyH (Beitrag 1085549)
Evtl. so?

War das jetzt auf mein "in eine Funktion auslagern" bezogen? Wenn ja: Nein, so hatte ICH mir das nicht vorgestellt. Eher so:
Delphi-Quellcode:
procedure TForm1.WechselgeldAusgeben(var ABetrag: Currency; AMuenzwert: Currency);
begin
  While ABetrag >= AMuenzwert do
  begin
    ABetrag := ABetrag - AMuenzwert;
    If AMuenzwert >= 1 then ListBox1.Items.Add(Format('%d-Euro-Stück', [Round(AMuenzwert)]))
      else ListBox1.Items.Add(Format('%d-Cent-Stück', [Round(AMuenzwert * 100)]));
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  S: Integer;
  Preis, R: Currency;
begin
  Preis := StrToCurr(Edit1.Text);
  S := StrToInt(Edit2.Text);
  ListBox1.Clear;
  R := S - Preis;
  If R > 9.99 then raise Exception.Create('Der Automat gibt ausschließlich Kleingeld und nur bis 9,99&#8364;.');
  WechselgeldAusgeben(R, 2.00);
  WechselgeldAusgeben(R, 1.00);
  WechselgeldAusgeben(R, 0.50);
  WechselgeldAusgeben(R, 0.20);
  WechselgeldAusgeben(R, 0.10);
  WechselgeldAusgeben(R, 0.05);
  WechselgeldAusgeben(R, 0.02);
  WechselgeldAusgeben(R, 0.01);
end;
Ungetestet, aber so ist meine Vorstellung :-D
So kann man einfach verfügbare Münzen hinzufügen, entfernen oder ändern, ohne die Schleife in dem der Münzwert drei mal redundant vorkommt anfassen zu müssen. Durch die Redundanz in der ursprünglichen Implementation schleichen sich sonst seeehr leicht Fehler ein.

Jumpy 3. Mär 2011 14:31

AW: Geldautomat
 
Wenn man den Betrag in Cent hat (also Eingabe*100) und das dann irgendwie in Integer umwandelt ginge auch:

Delphi-Quellcode:
var
  R,a : Integer;
  M : array[1..8]
begin
  M[1]=200;M[2]=100;M[3]=50;M[4]=20;M[5]=10;M[6]=5;M[7]=2;M[8]=1;

  R=CurrencyToInt(RückgeldInEuro * 100); //CurrencyToInt ??? s.o.

  for i = 1 To 8 do
    begin
    a:=R div M[i];
    R:=R-a;
    if a > 0 then
      begin    
      if M[i] >= 100 then
        ListBox1.Items.Add(IntToStr(M[i]/100)+'-Euro-Stück: '+IntToSTr(a)+'x')
      else
        ListBox1.Items.Add(IntToStr(M[i])+'-Cent-Stück: '+IntToSTr(a)+'x');
      end;
    end;
end


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:36 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz