Entwickler-Ecke

Open Source Units - Bruchrechnung


F34r0fTh3D4rk - Di 13.12.05 15:31
Titel: Bruchrechnung

UBruchRechnung.pas
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:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
343:
344:
345:
346:
347:
348:
349:
350:
351:
352:
353:
354:
355:
356:
357:
358:
359:
360:
361:
362:
363:
364:
365:
366:
367:
368:
369:
370:
371:
372:
373:
374:
375:
376:
377:
378:
379:
380:
381:
382:
383:
384:
385:
386:
387:
388:
389:
390:
391:
392:
393:
394:
395:
396:
397:
398:
399:
400:
401:
402:
403:
404:
405:
406:
407:
408:
409:
410:
411:
412:
413:
414:
415:
416:
417:
418:
419:
420:
421:
422:
423:
424:
425:
426:
427:
428:
429:
430:
431:
432:
433:
434:
435:
436:
437:
438:
439:
440:
441:
442:
443:
444:
445:
446:
447:
448:
449:
450:
451:
452:
453:
454:
455:
456:
{*************************************************************************************************  
UBruchrechnung  
==============  
 
Seth a.k.a. F34r0fTh3D4rk 2005 - 2006
.-,-.-,-.-,-.-,-.-,-.-,-.-,-.-,-.-,-.  
 
Dank an:
--------  
 
- delfiphan / für seine Funktion FloatToFrac (FloatToBruch V2)  
 
http://www.delphi-library.de/topic_+Fliesskommazahl+durch+Bruch+ZaehlerNenner+annaehern_47727.html  
 
- www.Delphi-Forum.de / für die Primfaktorzerlegung, sowie Hilfe und Support  
 
http://www.delphi-forum.de/viewtopic.php?t=51842
 
==================================================================================================  
 
Zur Unit:  
---------  
o Ich hafte für keine Schäden, die eventuel durch die Benutzung dieser
Unit am Programm, am Betriebssystem oder an der Hardware entstehen.  
Sonstige Schäden eingeschlossen.  
 
o Diese Unit kann verändert weitergegeben werden, in sofern die Namen der  
Originalauthoren enthalten bleiben (Verweis auf das Delphi-Forum, etc).  
 
==================================================================================================
 
Was macht diese unit ?  
----------------------  
 
o Diese unit ermöglicht das Arbeiten mit Brüchen unter Delphi.  
 
Unterstütze Features sind das
 
Addieren, Subtrahieren, Multiplizieren und Dividieren  
 
von Brüchen, sowie die Berechnung des ggT und des kgV nach euklid.  
 
Ergebnisse werden automatisch gekürzt, außer man gibt false als
letzten freiwilligen parameter an
Ansonsten kann die Prozedur p_kuerzen zum kürzen verwendet werden.
 
Ebenfalls ist eine Primfaktorzerlegung beigelegt, die mit der Hilfe  
von TPrimfaktor [= array of int64;] eingesetzt werden kann.  
Diese Funktion stammt Ursprünglich aus dem Delphi-Forum:  
 
  http://www.Delphi-Forum.de ich habe sie nur für diese unit angepasst.  
 
Hier der Original-Enstehungs Thread:
 
  http://www.delphi-forum.de/viewtopic.php?t=51842  
 
Es werden ebenfalls 2 Typumwandlungen mitgeliefert:  
 
BruchToFloat und FloatToBruch  
 
BruchToFloat wandelt einen Bruch in eine Fließkommazahl um,
FloatToBruch wandelt eine Fließkommazahl in einen Bruch um.  
(String Umwandlungsverfahren zur Berechnung der Nachkommastellen)  
 
Da FloatToBruch eine große Genauigkeit hat, und es in Delphi  
keine irrationalen Zahlen gibt ist die umwandlung von  
  _  
0,3 in einen Bruch nicht möglich, deshalb habe ich eine Funktion  
von delfiphan beigelegt, die eine gewisse Toleranz erlaubt und
somit die Zahl 0,333333333 in den Bruch 1/3 umwandeln kann.  
Algorithmus nach Euklid.  
 
Hier die Originalfunktion:  
 
http://www.delphi-library.de/topic_+Fliesskommazahl+durch+Bruch+ZaehlerNenner+annaehern_47727.html  
 
Mit der Funktion p_compare können 2 Brüche auf Wert-Gleichheit überprüft werden.

Der Rest ergibt sich aus den Kommentaren im Interface Abschnitt
==================================================================================================
 
Beispiel einer Addition zweier Brüche:  
--------------------------------------  
 
1   6  
- + -  
2   8  
var  
  Bruch1, Bruch2, Ergebnis: TBruch;  
begin  
  Bruch1.Zaehler := 1;  
  Bruch1.Nenner := 2;  
 
  Bruch2.Zaehler := 6;  
  Bruch2.Nenner := 8;  
 
  Ergebnis := p_add(Bruch1, Bruch2);
end;  
 
Beispiel einer Primfaktorzerlegung:  
-----------------------------------  
 
primfaktorzerlegung von 64  
 
var  
  Primfaktor: TPrimfaktor;  
begin
  Primfaktor := p_primfaktor(64);  
end;  
 
 
Beispiel einer Typumwandlung mit Hilfe von FloatToBruch V2:  
---------------------------------------------------------  
 
0,33333333333333 soll in 1/3 umgewandelt werden.  
 
var
  Bruch: TBruch;  
begin  
  Bruch := FloatToBruch(0.33333333333333, 1e-12);  
end;  
 
==================================================================================================  
 
 
*************************************************************************************************}
  


unit UBruchRechnung;

interface
 
//Benötigt für DWORD und floattostr  
uses windows, sysutils;
 
//TBruch - Record aus Zähler und Nenner, zur einfacheren Handhabung
type  
  TBruch = record  
    Zaehler, Nenner: int64;  
  end;
//TBruchG um den Ganzzahlanteil eines Bruches extra zu speichern
  TBruchG = record
    Zahl: int64;
    Bruch: TBruch;
  end;
    
//TPrimfaktor - Integer Array zum Speichern von Primfaktoren  
  TPrimfaktor = array of int64;
 
//p_ggT - Berechnet den größten gemeinsamen Teiler zweier Zahlen  
function p_ggT(a, b: int64): int64;
 
//p_kgv - Berechnet das kleinste gemeinsame Vielfache zweier Zahlen  
function p_kgv(a, b: int64): int64;
 
//p_primfaktor - Zerlegt eine Zahl in Primfaktoren  
function p_primfaktor(a: int64): TPrimfaktor;

//p_kuerzen - Kürzt einen Bruch
procedure p_kuerzen(var Bruch: TBruch);
 
//p_add - Addiert zwei Brüche  
function p_add(Bruch1, Bruch2: TBruch; kuerzen: boolean = true): TBruch;
 
//p_sub - Subtrahiert zwei Brüche  
function p_sub(Bruch1, Bruch2: TBruch; kuerzen: boolean = true): TBruch;

//p_multi - Multipliziert zwei Brüche  
function p_multi(Bruch1, Bruch2: TBruch; kuerzen: boolean = true): TBruch;
 
//p_div - Dividiert zwei Brüche  
function p_div(Bruch1, Bruch2: TBruch; kuerzen: boolean = true): TBruch;

//p_compare - Prüft ob zwei Brüche gleichwertig sind (Z1 / N1) = (Z2 / N2)  
function p_compare(Bruch1, Bruch2: TBruch): boolean;

//ToBruch -  wandelt zwei int64 in einen Bruch um
function ToBruch(zaehler, nenner: int64): TBruch;

//BruchToFloat - Typumwandlung in einen Bruch  
function BruchToFloat(Bruch: TBruch): Extended;
 
//FloatToBruch - Typumwandlung in eine Fließkommazahl  
function FloatToBruch(const a: Extended): TBruch; overload;
 
//FloatToBruch2 - Typunwandlung in eine Fließkommazahl mit toleranz  
function FloatToBruch(const a, toleranz: Extended): TBruch; overload;

//BruchToStr - Wandelt einen Bruch in einen String nach dem Format Z/N um
function BruchToStr(Bruch: TBruch): string;
 
//StrToBruch - Wandelt einen String des Formates Z/N in einen Bruch um
function StrToBruch(const s: string; toleranz: Extended = -1; kuerzen: boolean = true): TBruch;

//BruchToGruchG - Wandelt einen Bruch in einen Bruch mit Ganzzahlanteil um
function BruchToBruchG(Bruch: TBruch; kuerzen: boolean = true): TBruchG;

//BruchGToBruch - Wandelt einen Bruch mit Ganzzahlanteil in einen Bruch um
function BruchGToBruch(BruchG: TBruchG; kuerzen: boolean = true): TBruch;

//BruchGToStr - Wandelt einen Bruch mit Ganzzahlanteil in einen String nach dem Format Za Z/N um
function BruchGToStr(BruchG: TBruchG): string;

 
implementation

 
function p_ggT(a, b: int64): int64;  
var  
  r: int64;  
begin  
  if (a = 0or (b = 0then  
  begin  
    result := 0;  
    exit;  
  end;
  repeat  
    r := b;  
    b := a mod b;  
    a := r;  
  until b = 0;  
  result := a;  
end;  

 
function p_kgv(a, b: int64): int64;  
begin
  result := (a * b) div p_ggT(a, b);  
end;  

 
function p_primfaktor(a: int64): TPrimfaktor;
var
  Teiler: Int64;
begin
  if a < 0 then
  begin
    SetLength(result, 1);
    result[0] := -1;
    a := -a;
  end;
  while (a and 1) = 0 do
  begin
     setlength(result, length(result) + 1);
     Result[high(Result)] := 2;
     a := a shr 1;
  end;
  if a >= 2 then
  begin
    Teiler := 3;
    while a mod Teiler = 0 do
    begin  
      setlength(result, length(result) + 1);  
      Result[high(Result)] := Teiler;
      a := a div Teiler;
    end;
    Inc(Teiler, 2);
    while Teiler <= Trunc(SQRT(1.0 * a)) do
    begin
      while a mod Teiler = 0 do
      begin  
        setlength(result, length(result) + 1);  
        Result[high(Result)] := Teiler;
        a := a div Teiler;
      end;
      Inc(Teiler, 2);
      while a mod Teiler = 0 do
      begin  
        setlength(result, length(result) + 1);  
        Result[high(Result)] := Teiler;
        a := a div Teiler;  
      end;
      Inc(Teiler, 4);
    end;
  end;  
  if a <> 1 then
  begin
    setlength(result, length(result) + 1);
    Result[high(Result)] := a;
  end;
end;


procedure p_kuerzen(var Bruch: TBruch);
var  
  ggt: int64;  
begin  
  if Bruch.Zaehler = 0 then
  begin
    Bruch.Nenner := 1;
    exit;  
  end;    
  ggt := p_ggt(Bruch.Zaehler, Bruch.Nenner);  
  if ggt = 1 then
    exit;
  Bruch.Zaehler := Bruch.Zaehler div ggt;
  Bruch.Nenner := Bruch.Nenner div ggt;  
end;  

 
function p_add(Bruch1, Bruch2: TBruch; kuerzen: boolean = true): TBruch;  
begin  
  result.Nenner := Bruch1.Nenner * Bruch2.Nenner;  
  result.Zaehler := Bruch1.Zaehler * Bruch2.Nenner + Bruch2.Zaehler * Bruch1.Nenner;  
  if kuerzen then  
    p_kuerzen(result);
end;

 
function p_sub(Bruch1, Bruch2: TBruch; kuerzen: boolean = true): TBruch;  
begin  
  result.Nenner := Bruch1.Nenner * Bruch2.Nenner;  
  result.Zaehler := Bruch1.Zaehler * Bruch2.Nenner - Bruch2.Zaehler * Bruch1.Nenner;  
  if kuerzen then  
    p_kuerzen(result);
end;  

 
function p_multi(Bruch1, Bruch2: TBruch; kuerzen: boolean = true): TBruch;  
begin  
  result.Nenner := Bruch1.Nenner * Bruch2.Nenner;  
  result.Zaehler := Bruch1.Zaehler * Bruch2.Zaehler;  
  if kuerzen then  
    p_kuerzen(result);
end;  

 
function p_div(Bruch1, Bruch2: TBruch; kuerzen: boolean = true): TBruch;
begin  
  result.Nenner := Bruch1.Nenner * Bruch2.Zaehler;  
  result.Zaehler := Bruch1.Zaehler * Bruch2.Nenner;  
  if kuerzen then  
    p_kuerzen(result);  
end;


function p_compare(Bruch1, Bruch2: TBruch): boolean;
begin
  result := Bruch1.Zaehler * Bruch2.Nenner = Bruch2.Zaehler * Bruch1.Nenner;
end;


function ToBruch(zaehler, nenner: int64): TBruch;
begin
  result.zaehler := zaehler;
  result.Nenner := nenner;
end;

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

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

 
function FloatToBruch(const a, toleranz: Extended): TBruch; overload;
var  
  p, lastp, q, lastq, ptemp, qtemp, u, err, d: Extended;  
begin  
  p := 1;
  q := 0;  
  lastp := 0;  
  lastq := 1;  
  u := a;  
  repeat  
    d := round(u);  
    u := u - d;  
    ptemp := p * d + lastp;  
    qtemp := q * d + lastq;  
    lastp := p;  
    lastq := q;
    p := ptemp;  
    q := qtemp;  
    err := abs(p / q - a);  
    if (u = 0or (err < toleranz) or (a + err / 4 = a) then  
      break;  
    u := 1 / u;  
  until false;  
  if (p > high(Int64)) or (p < low(Int64)) then  
    exit;  
  if (q > high(Int64)) or (q < low(Int64)) then
    exit;
  if q < 0 then  
    result.Zaehler := - Trunc(p) else  
      result.Zaehler := Trunc(p);  
  result.Nenner := abs(Trunc(q));  
end;  


function BruchToStr(Bruch: TBruch): string;
begin  
  result := Format('%d/%d', [Bruch.Zaehler, Bruch.Nenner]);  
end;  


function StrToBruch(const s: string; toleranz: Extended = -1; kuerzen: boolean = true): TBruch;
var
  p : integer;
begin  
  p := pos('/', s);  
  if p = 0 then
  begin
    if toleranz = -1 then
      result := FloatToBruch(strtofloat(trim(s))) else
        result := FloatToBruch(strtofloat(trim(s)), toleranz);
  end else
  begin
    result.Zaehler := StrToInt64Def(trim(copy(s, 1, p - 1)), 0);  
    result.Nenner := StrToInt64Def(trim(copy(s, p + 1, Length(s))), 1);
  end;
  if kuerzen then
    p_kuerzen(result);
end;

function BruchToBruchG(Bruch: TBruch; kuerzen: boolean = true): TBruchG;
begin
  if kuerzen then
    p_kuerzen(Bruch);
  result.Zahl := 0;
  result.Bruch := Bruch;
  if Bruch.Zaehler < Bruch.Nenner then
    exit;
  result.Zahl := Bruch.Zaehler div Bruch.Nenner;
  result.Bruch.Zaehler := Bruch.Zaehler mod Bruch.Nenner;
end;

function BruchGToBruch(BruchG: TBruchG; kuerzen: boolean = true): TBruch;
begin
  result.Zaehler := BruchG.Bruch.Zaehler + BruchG.Zahl * BruchG.Bruch.Nenner;
  result.Nenner := BruchG.Bruch.Nenner;
  if kuerzen then
    p_kuerzen(result);
end;


function BruchGToStr(BruchG: TBruchG): string;
begin  
  result := Format('%d %d/%d', [BruchG.Zahl, BruchG.Bruch.Zaehler, BruchG.Bruch.Nenner]);
end;


end.


Die Primfaktor zerlegung gibt an der ersten Stelle eine -1 aus, wenn die Zahl negativ ist.


Martin1966 - Mo 19.12.05 11:10

Super, danke für das Veröffentlichen diseser Unit. Wenn ich mal Zeit habe dann teste ich die Funktionen!! ;-)

Wofür steht das p_ vor den meisten Funktionen?

Wäre es nicht sinnvoll die beiden Funktionen FloatToBruch und FloatToBruch2 die gleiche Bezeichnung zu verpassen und dann als overload zu deklarieren?

Lg Martin


F34r0fTh3D4rk - Mo 19.12.05 15:20

das mit overload ist keine schlechte idee, das p_ stand ursprünglich mal für procedure, ich benutze es, um zu verhindern, dass ich einen funktionsnamen wähle den es schon gibt ;)


Kroko - Mo 19.12.05 15:29


Delphi-Quelltext
1:
2:
3:
4:
function p_compare(Bruch1, Bruch2: TBruch): boolean;
begin
  result := (BruchToFloat(Bruch1) = BruchToFloat(Bruch2));
end;

sollte wohl besser in

Delphi-Quelltext
1:
2:
3:
4:
function p_compare(Bruch1, Bruch2: TBruch): boolean;
begin
  result := Bruch1.Nenner*Bruch2.Zaehler=Bruch1.Zaehler*Bruch2.Nenner;
end;
umgewandelt werden,denn es wirft Rundungsfehler über den Haufen!


F34r0fTh3D4rk - Mo 19.12.05 15:36

user profile iconKroko hat folgendes geschrieben:

Delphi-Quelltext
1:
2:
3:
4:
function p_compare(Bruch1, Bruch2: TBruch): boolean;
begin
  result := (BruchToFloat(Bruch1) = BruchToFloat(Bruch2));
end;

sollte wohl besser in

Delphi-Quelltext
1:
2:
3:
4:
function p_compare(Bruch1, Bruch2: TBruch): boolean;
begin
  result := Bruch1.Nenner*Bruch2.Zaehler=Bruch1.Zaehler*Bruch2.Nenner;
end;
umgewandelt werden,denn es wirft Rundungsfehler über den Haufen!

bei deiner version kann eigentlich garnichts gescheites bei rauskommen :shock:
ich könnte auch die brüche kürzen und zähler und nenner einzeln vergleichen, aber deine variante ergibt irgendwie keinen sinn :?

Das Overload hab ich jetzt übernommen, danke ;)


Kroko - Mo 19.12.05 18:55

Bruchrechnung Klasse 5

a/b = c/d , wenn gilt a*d=b*c :wink:


F34r0fTh3D4rk - Mo 19.12.05 20:07

aso ja, hast recht ^^ ändere ich sofort ;)
ist ja das gleiche wie ac/dc :P


beshig - Sa 25.02.06 14:52

da sieht man mal, wie schnell man auch beim Programmieren auf Musik kommt *AC/DV Fan bin*.

Mir gefällt diese Unit, habe sie heute schon einmal benutzt in einem Programm !


LLCoolDave - Sa 25.02.06 15:04

user profile iconbeshig hat folgendes geschrieben:
da sieht man mal, wie schnell man auch beim Programmieren auf Musik kommt *AC/DV Fan bin*.


Nur dass das für Alternating current/Direct current steht :P


BenBE - Sa 25.02.06 15:37

Die Initialie Zuweisung von 0\0 bei den meisten Grundrechenarten kann entfallen. Müsste Dir der Compiler aber eigentlich ansagen.


F34r0fTh3D4rk - Sa 25.02.06 17:22

hab ich selbst gemerkt, aber ich habs lieber gelassen ;)


BenBE - Sa 25.02.06 17:39

Was überflüssig ist, sollte man wenn möglich auch entfernen.

Übrigens: Export brauchst Du nicht anzugeben, hat unter allen Delphi-Versionen ab D2 eh keine Auswirkung mehr.


F34r0fTh3D4rk - Sa 25.02.06 17:40

man muss ja abwärts kompatibel bleiben :P werde ich bei gelegenheit ändern, danke


BenBE - Sa 25.02.06 18:04

Auf Grund der dynamischen Arrays läuft dein Source eh erst ab D4 ... Von daher ist das eh egal ...

BTW: Könntest Du noch eine Funktion zum Zusammenfassen der Primfaktor-Zerlegung zu Potenzen einbauen???

Sprich:

Delphi-Quelltext
1:
64 = 2*2*2*2*2*2 = 2 ^ 6                    


F34r0fTh3D4rk - Sa 25.02.06 19:28

wäre machbar, werd ich bei gelegenheit mal versuchen ;)


wp_xxyyzz - So 26.02.06 16:31

Bin gerade dabei, diese schöne und nützliche Unit zu testen. Beim Berechnen der Summe von 1/2 und -1/2 gab es aber eine Division durch null, da ggt(0)=0 ist. Die "kuerzen"-Routine, in der durch den ggT dividiert wird, solltest du folgendermaßen ergänzen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
function kuerzen(Bruch:TBruch) : TBruch;
begin
  if Bruch.Zaehler=0 then begin
    result.Zaehler := 0;
    result.Nenner := 1;
    exit;
  end;
  result := Bruch;
  // weiter wie bei dir ...
end;



Nützlich erscheinen mir auch:



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
function BruchToStr(Bruch:TBruch) : string;
begin
  result := Format('%d / %d', [Bruch.Zaehler, Bruch.Nenner]);
end;

function StrToBruch(const s:string) : TBruch;
var
  p : integer;
begin
  p := pos('/', s);
  if p=0 then begin
    result.Zaehler := StrToInt64(trim(s));
    result.Nenner := 1;
  end else begin
    result.Zaehler := StrToInt64(trim(copy(s, 1, p-1)));
    result.Nenner := StrToInt64(trim(copy(s, p+1, Length(s))));
  end;
end;



Gruß, Werner


wp_xxyyzz - So 26.02.06 17:15

Ach ja, und die Primfaktorzerlegung findet bei negativen Zahlen kein Ende. Ich habe an den Anfang der Routine folgendes gesetzt:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
if a<0 then begin
  SetLength(result, 1);
  result[0] := -1;
  a := -a;
end;
// dann weiter wie bei dir...


Gruß, Werner


F34r0fTh3D4rk - So 26.02.06 17:25

danke, werde ich überarbeiten ;)


BenBE - So 26.02.06 17:49

user profile iconwp_xxyyzz hat folgendes geschrieben:
Nützlich erscheinen mir auch:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
function BruchToStr(Bruch:TBruch) : string;
begin
  result := Format('%d / %d', [Bruch.Zaehler, Bruch.Nenner]);
end;

function StrToBruch(const s:string) : TBruch;
var
  p : integer;
begin
  p := pos('/', s);
  if p=0 then begin
    result.Zaehler := StrToInt64(trim(s));
    result.Nenner := 1;
  end else begin
    result.Zaehler := StrToInt64(trim(copy(s, 1, p-1)));
    result.Nenner := StrToInt64(trim(copy(s, p+1, Length(s))));
  end;
end;


Gruß, Werner


Sollte die 2. Routine nicht, wenn kein Bruchstrich vorkommt, FloatToBruch aufrufen? Fänd ich logischer...


wp_xxyyzz - So 26.02.06 18:13

user profile iconBenBE hat folgendes geschrieben:

Sollte die 2. Routine nicht, wenn kein Bruchstrich vorkommt, FloatToBruch aufrufen? Fänd ich logischer...


Wenn kein Bruchstrich vorkommt, muss es sich um eine ganze Zahl handeln, da TBruch nur aus ganzen Zahlen zusammengesetzt ist.


BenBE - So 26.02.06 18:27

user profile iconwp_xxyyzz hat folgendes geschrieben:
user profile iconBenBE hat folgendes geschrieben:

Sollte die 2. Routine nicht, wenn kein Bruchstrich vorkommt, FloatToBruch aufrufen? Fänd ich logischer...


Wenn kein Bruchstrich vorkommt, muss es sich um eine ganze Zahl handeln, da TBruch nur aus ganzen Zahlen zusammengesetzt ist.

Nein, grad nicht, weil ich kann doch StrToBruch auch sowas hier übergeben:


Delphi-Quelltext
1:
Bruch := StrToBruch('0.5');                    


UC? ;-)


wp_xxyyzz - So 26.02.06 18:34

Natürlich - hast recht!


Dezipaitor - Di 14.03.06 23:12

coole unit

nur so zum Spaß:
könnte man eigentlich auch mit komplexen Zahlen rechnen.

also
z = a + ib (wobei a,b e R)
i^2 = -1

i = sqrt(-1)

und
1/i = i/(i^2) = -i

sowie
1/(a+ib) = (a-ib)/(a^2 + b^2) b <> 0 && a <> 0
(Bruch mit (a-ib) erweitert und 3.binomF. im Nenner )

Vielleicht hat jemand Lust?


F34r0fTh3D4rk - So 16.04.06 12:16

von komplexen zahlen habe ich leider keine ahnung, sonst hätte ichs bestimmt umgesetzt ^^ alle anderen änderungen sollten jetzt drin sein


F34r0fTh3D4rk - Fr 21.04.06 16:27

Ich habe die Unit nochmal ein wenig verbessert, falls noch irgendwo zb ein const zuviel oder zu wenig ist, das wäre nett, die parameterübergaben sollen ja möglichst optimal gewählt sein.

Neu ist TBruchG, im moment kann nur von TBruch in TBruchG umgewandelt werden, aber Stringumwandlungen, sowie einige andere Dinge werden folgen, ebenso die Behandlung von Mehrfachbrüchen (autsch :lol: )

TBruchG repräsentiert einen Bruch mit Ganzzahlanteil, das ist nützlich wenn man sowas darstellen will:


Delphi-Quelltext
1:
2:
3:
45 67/23
-- -- --
1. 2. 3.


1. ganzzahl
2. zähler
3. nenner

die umwandlung von und in einen string kommt noch, eventuell werde ich die unit noch ein wenig umstrukturieren

im moment geht das mit dem anzeigen so:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var
  aBruch: TBruch;
  aBruchG: TBruchG;
begin
  aBruch := StrToBruch(edit1.text);
  aBruchG := BruchToBruchG(aBruch);
  edit2.text := inttostr(aBruchG.Zahl) + ' ' + BruchToStr(ToBruch(aBruchG.Zaehler, aBruchG.Nenner));
end;


EDIT: TBruchG besteht jetzt aus Zahl und Bruch und die umwandlung sollte jetzt auch fehlerfrei funktionieren


F34r0fTh3D4rk - Sa 22.04.06 11:47

ich arbeite momentan an einer sehr umfangreichen Demo Applikation, in der sämtliche Features mit Beispielen veranschaulicht werden, außerdem werden noch Funktionen wie FloatBruchAdd und IntBruchAdd sowie Sub multi und div dazukommen, dann kann ich auch ein wenig bei der umwandlung von bruchg zu bruch kürzen usw.

weils so viel spass macht, der ggT in C#

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
    public int ggT(int a, int b)
    {
      if ((a == 0) || (b == 0))
        return 0;
      int r;
      do
      {
        r = b;
        b = a % b;
        a = r;
      }
      while (b != 0);
      return a;
    }


wp_xxyyzz - So 23.04.06 13:41

TBruchG findet ich zunächst eine nützliche Erweiterung, da nun praktisch die ganze Bruchrechnung abgedeckt ist. Allerdings stört mich, dass man dafür einen weiteren Int64 benötigt, die Information dafür aber schon in der bisherigen Deklaration vorhanden ist. Ich würde folgende Änderungen vorschlagen, um einen ganzzahligen Bruchanteil zu implementieren, ohne TBruchG einführen zu müssen:

- Die bereits vorhandene Funktion ToBruch würde ich mit einer Variante mit Ganzzahl überladen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
function ToBruch(Ganze,Zaehler,Nenner:int64) : TBruch; // natürlich mit "overload"
begin
  if Nenner=0 then raise Exception.Create('Der Nenner darf nicht null sein.');
  result.Zaehler := Ganze*Nenner + Zaehler;
  result.Nenner := Nenner;
end;


- Die Stringumwandlungsroutine müsste die vorgeschaltete Ganzzahl erkennen; die folgende Routine geht noch weiter und erkennt auch Float-Zahlen in Ganzzahl, Zähler und Nenner; außerdem werden einige offensichtliche Syntax-Fehler erkannt (aber sicher nicht alle):


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:
52:
53:
54:
function StrToBruch(const s:string; eps:extended=0.0) : TBruch;
var
  p : integer;
  b1,b2,b3 : TBruch;

  function Get(var z : TBruch) : boolean;
  var
    tmp : string;
  begin
    tmp := '';
    while (p <= Length(s)) and (s[p] = ' 'do inc(p);
    while (p<=Length(s)) and (not (s[p] in [' ''/'])) do begin
      tmp := tmp + s[p];
      inc(p);
    end;
    while (p <= Length(s)) and (s[p] = ' 'do inc(p);
    if tmp=''
      then result := false
    else begin
      if eps=0.0
        then z := FloatToBruch(StrToFloat(tmp))
        else z := FloatToBruch(StrToFloat(tmp), eps);
      result := true;
    end;
  end;

begin
  p := 1;
  if Get(b1) then begin
    if (p>Length(s)) then
      result := b1
    else
    if s[p]='/' then begin
      inc(p);
      if Get(b2) then
        result := p_Div(b1, b2)
      else
        raise Exception.Create('Syntax-Fehler');  // keine weitere Zahl hinter dem "/"
    end else begin
      if Get(b2) then begin
        if s[p]='/' then begin
          inc(p);
          if Get(b3) then
            result := p_Add(b1, p_Div(b2,b3))
          else
            raise Exception.Create('Syntax-Fehler'); // s.o.
        end else
          raise Exception.Create('Syntax-Fehler');
      end else
        result := b1;
    end;
  end else
    raise Exception.Create('Syntax-Fehler');   // Hier fehlt der Nenner.
end;


- Folgende Funktionen extrahieren Ganzzahl etc. aus einem Bruch, damit muss man der Anwender die interne Darstellung eines Bruchs nicht mehr kennen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
function Ganze(bruch:TBruch) : int64;
begin
  result := Bruch.Zaehler div Bruch.Nenner;
end;

function Zaehler(bruch:TBruch) : int64;
begin
  result := Bruch.Zaehler mod Bruch.Nenner;
end;

function Nenner(Bruch:TBruch) : int64;
begin
  result := Bruch.Nenner;
end;


- Schließlich erscheint mir noch eine weitere Stringumwandlungsroutine sinnvoll:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
function BruchToNiceStr(Bruch:TBruch) : string;
// Spaltet den Ganzteil ab, aber nur wenn vorhanden. Das Ergebnis ist ein "schöner" Bruch.
var
  g,z,n : int64;
begin
  Bruch := p_Kuerzen(Bruch);
  g := Ganze(Bruch);
  z := Zaehler(Bruch);
  n := Nenner(Bruch);
  if (g<>0then begin
    if (n<>1then result := Format('%d %d/%d', [g,z,n])
    else result := Format('%d', [g])
  end else
  if (n<>1)
    then result := Format('%d/%d', [z, n])
    else result := Format('%d', [z]);
end;


F34r0fTh3D4rk - Mo 24.04.06 14:34

da hast du dir ja schon gedanken gemacht ^^ einige dinge sind auch schon fertig, aber sachen wie sowas x x/x von string in einen bruch umzuwandeln hat noch nicht ganz so geklappt, ich überlege auch immer noch, funktionen einzubauen um zahlen als int oder float mit brüchen zu addieren multiplizieren usw und mehrfachbrüche einzubauen, mehrfachbrüche vielleicht mit nem array aus nennern oder einfach einem extended als nenner, da bin ich mir noch uneinig ;)


Beiri22 - Mo 22.05.06 18:27
Titel: in D2006
In delphi 2006 kannst du von records die operatoren überladen,
du könntest da also alles in den record mit einbauen, das lässt
sich dann viel einfacher handhaben.


F34r0fTh3D4rk - Mo 22.05.06 18:46

in delphi 2006 :lol:


Beiri22 - Mo 22.05.06 20:43

ja in delphi 2006


F34r0fTh3D4rk - Di 23.05.06 10:08

es hat aber nunmal nicht jeder delphi2006, ich kann eine delphi2006 version machen, könnte es aber nicht testen weil ich kein d2006 habe ;)


Beiri22 - Di 23.05.06 14:42

ich nehm doch auch nur (noch) die trial, is mir nur halt dabei aufgefallen.