Entwickler-Ecke

Algorithmen, Optimierung und Assembler - float to bruch


F34r0fTh3D4rk - So 27.11.05 09:40
Titel: float to bruch
Ich schreibe gerade eine Funktion, um Fließkommazahlen in Brüche umzuwandeln, für meine unit UBruchrechnung, soweit hab ichs jetzt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TForm1.Button6Click(Sender: TObject);
var
  Bruch: TBruch;
  nc, i: integer;
  flt: extended;
  str: string;
begin
  flt := strtofloat(edit5.text);  //Die Float Variable
  nc := 1;
  for i := 1 to length(inttostr(round(frac(flt)))) do
    nc := nc * 10;
  Bruch.Zaehler := round(flt * nc);
  Bruch.Nenner := nc;

  Bruch := p_kuerzen(Bruch);  //Kuerzen

  //Ausgeben
  Label1.Caption := inttostr(Bruch.Zaehler);
  Label2.Caption := inttostr(Bruch.Nenner);
end;

Ganz Perfekt ist es noch nicht, auch hab ich noch so die bedenken, wie ich das mit periodischen Zahlen mache, aber ich denke, selbst an dieser Funktion lässt sich schon ne Menge Verbessern, ich hoffe mal, dass strings nicht sein müssen.

Aus 6/7 macht er:

0,857142857142857

und daraus macht er wieder 9/10


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
function BruchToFloat(Bruch: TBruch): Extended;
begin
  result := Bruch.Zaehler / Bruch.Nenner;
end;

procedure TForm1.Button7Click(Sender: TObject);
var
  Bruch: TBruch;
begin
  Bruch.Zaehler := strtoint(edit1.text);
  Bruch.Nenner := strtoint(edit2.text);
  edit5.Text := floattostr(BruchToFloat(Bruch));
end;


(warum eigentlich nur 15 nachkommastellen ?)

Die unit die man zum kürzen und für die Brüche braucht gibts hier:

http://www.delphi-library.de/viewtopic.php?p=313675

Danke schonmal ;) Das fertige Ergebnis kommt dann in die Unit rein.


Lannes - So 27.11.05 11:49
Titel: Re: float to bruch
Hallo,
user profile iconF34r0fTh3D4rk hat folgendes geschrieben:
...(warum eigentlich nur 15 nachkommastellen ?)

FloatToStr berücksichtigt nur 15 Stellen, hier die Alternativen bis 18 Stellen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TForm1.Button7Click(Sender: TObject);
var
  Bruch: TBruch;
begin
  Bruch.Zaehler := strtoint(edit1.text);
  Bruch.Nenner := strtoint(edit2.text);
  //edit5.Text := FloatToStr(BruchToFloat(Bruch));
  edit5.Text := Format('%.18f',[BruchToFloat(Bruch)]);
  //oder
  //edit5.Text := FormatFloat('0.000000000000000000',BruchToFloat(Bruch));
  //edit5.Text := FloatToStrF(BruchToFloat(Bruch),ffFixed,18,18);
end;


F34r0fTh3D4rk - So 27.11.05 12:03

dachte ich mir, nur muss ich diese funktion hier irgendwie mal anständig optimieren:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
procedure TForm1.Button6Click(Sender: TObject);  
var  
  Bruch: TBruch;  
  nc, i: integer;  
  flt: extended;  
  str: string;  
begin  
  flt := strtofloat(edit5.text);  //Die Float Variable  
  nc := 1;  
  for i := 1 to length(inttostr(round(frac(flt)))) do  
    nc := nc * 10;  
  Bruch.Zaehler := round(flt * nc);  
  Bruch.Nenner := nc;  

 
  Bruch := p_kuerzen(Bruch);  //Kuerzen  
 
  //Ausgeben  
  Label1.Caption := inttostr(Bruch.Zaehler);  
  Label2.Caption := inttostr(Bruch.Nenner);  
end;

ich will möglichst die string umwandlungen und am besten noch frac da raus haben, nur wie ?

Aus 1,587 macht er 8/5 aus 8/5 aber 1,6

ohne runden stimmts jetzt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TForm1.Button6Click(Sender: TObject);
var
  Bruch: TBruch;
  nc, i: integer;
  flt: extended;
begin
  flt := strtofloat(edit5.text);  //Die Float Variable
  //showmessage(floattostr(flt));
  nc := 1;
  for i := 1 to length(floattostr(frac(flt))) do
    nc := nc * 10;
  Bruch.Zaehler := round(flt * nc);
  Bruch.Nenner := nc;

  Bruch := p_kuerzen(Bruch);  //Kuerzen

  //Ausgeben
  Label1.Caption := inttostr(Bruch.Zaehler);
  Label2.Caption := inttostr(Bruch.Nenner);
end;

Fehler von mir, sry, nur muss man die nachkommastellen doch auch irgendwie anders herausbekommen, nur wie ?


F34r0fTh3D4rk - So 27.11.05 13:44

nochmal den relevanten teil als funktionen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
type
  TBruch = record
    Zaehler, Nenner: integer;
  end;

function p_ggT(a, b: integer): integer;
var
  x, y, r: Integer;
begin
  x := a;
  y := b;
  r := x mod y;
  while (r <> 0do
  begin
    x := y;
    y := r;
    r := x mod y;
  end;
  result := y;
end;

function p_kuerzen(Bruch: TBruch): TBruch;
var
  ggt: integer;
begin
  result := Bruch;
  ggt := p_ggt(Bruch.Zaehler, Bruch.Nenner);
  if ggt = 1 then
    exit;
  result.Zaehler := Bruch.Zaehler div ggt;
  result.Nenner := Bruch.Nenner div ggt;
end;

function BruchToFloat(Bruch: TBruch): Extended;
begin
  result := Bruch.Zaehler / Bruch.Nenner;
end;

function FloatToBruch(a: Extended): TBruch;
var
  Faktor, i: integer;
begin
  result.Nenner := 0;
  result.Zaehler := 0;
  Faktor := 1;
  for i := 1 to length(floattostr(frac(a))) do
    Faktor := Faktor * 10;
  result.Zaehler := round(a * Faktor);
  result.Nenner := Faktor;
  result := p_kuerzen(result);
end;

die markierten teile stören mich noch und die schleife müsste auch nicht unbedingt sein :?

ich könnte auch einfach maxint als faktor nehmen, aber das macht die funktion bissl langsam denke ich. hm geht ja net, dann übersteige ich maxint, so ein dreck, wenn ich dann aber int64 nehmen, bekomme ich so kein maximum :(

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
function FloatToBruch(a: Extended): TBruch;
//var
  //Faktor, i: integer;
begin
  result.Nenner := 0;
  result.Zaehler := 0;
  //Faktor := 1;
  //for i := 1 to length(floattostr(frac(a))) do
    //Faktor := Faktor * 10;
  result.Zaehler := round(a * 1000000000);
  result.Nenner := 1000000000;
  result := p_kuerzen(result);
end;

bei zahlen die ein wenig größer sind gibts dann probleme :( das muss doch irgendwie zu lösen sein :?:


BenBE - So 27.11.05 15:15

Zum zurückwandeln von Brüchen aus deren Dezimaldarstellung nimmt man meist die Kettenbruch-Zerlegung mit Hilfe des euklidischen Algos.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
//Kettenbrucherzeugung
I := 0;
While Frac(X) > epsilon Do
Begin
    K[I] := Trunc(X);
    X := 1 / Frac(X);
    Inc(I);
end;

//Initialisierung des Bruchs
Z := Trunc(X);
N := 1;

//Rückumwandlung
While I > 0 do
Begin
    //Reziprokes bilden
    Tmp := N;
    N := Z;
    Z := Tmp;

    Dec(I);
    N := N + K[I] * Z;
end;


Ungetestet ...


F34r0fTh3D4rk - So 27.11.05 15:35

ist das schneller, als eine umwandlung in einen string ?
was ist epsilon und muss der bruch danach noch gekürzt werden ?


DaRkFiRe - Mo 28.11.05 22:20

Epsilon ist eine Art Toleranzgrenze, die Du vorgibst.


BenBE - Mo 28.11.05 22:29

Je nach Wahl des Epsilons (Toleranzgrenze, normalerweise <1E-10) wirst Du eine Bruch-Annäherung machen müssen, d.h. dass er Dir den gekürzten Bruch
1000\3001 zurückgibt, obwohl Du 1/3 erwartest. Diese annäherung ist aber relativ einfach zu bewerkstelligen, da du einfach beim Zurückmultiplizieren nicht alle Glieder berücksichtigst.


F34r0fTh3D4rk - Di 29.11.05 13:07

dann ist es aber nicht so genau wie meins ;) und vielleicht auch langsamer :?


Horst_H - Di 29.11.05 13:56

Hallo,

auch die Umwandlung in einen String hat keine unendliche Genauigkeit und kostet auch gewaltig Zeit.
Wandel mal 3.14159265.. um.
Der Kettenbruch liefert schon frueh 355/113 auch ohne GGT-bestimmung

Gruss Horst


F34r0fTh3D4rk - Di 29.11.05 15:03

wie muss epsilon denn gewählt sein, um die richtigen brüche rauszubekommen ?

lässt sich die anzahl der nachkommastellen irgendwie anders bestimmen ?