Entwickler-Ecke

Open Source Units - BigNum2 v0.2.1.20080517


BenBE - Sa 17.05.08 18:35
Titel: BigNum2 v0.2.1.20080517
Viele werden diese Unit sicherlich schon im Forum gesehen haben, manche auch mein vor Kurzem veröffentlichtes Versprechen gelesen haben; hier ist sie nun:

Die Großzahlarithmetik-Bibliothek BigNum2

Die aktuelle Version ist Version 0.2.1.20080517. Download gibt's im Anhang.

Was macht BigNum2?
Mit großen Zahlen rechnen ... Mit sehr großen Zahlen ...

Was kann BigNum2?

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:
Function BM_Add(Num1, Num2: TBigNumber): TBigNumber;
Function BM_Sub(Num1, Num2: TBigNumber): TBigNumber;
Function BM_Divide(Num1, Num2: TBigNumber): TBigNumber;
Function BM_Modulo(Num1, Num2: TBigNumber): TBigNumber;
Function BM_DivMod(Var Num1: TBigNumber; Num2: TBigNumber): TBigNumber; //Returns Int as Num1, Mod as Result
Function BM_Multiply(Num1, Num2: TBigNumber): TBigNumber;
Function BM_MultiplyMod(Num1, Num2, Module: TBigNumber): TBigNumber;
Function BM_Power(Base, Exp: TBigNumber): TBigNumber;
Function BM_PowerMod(Base, Exp, Module: TBigNumber): TBigNumber;
Function BM_Log(Power, Base: TBigNumber): TBigNumber;
Function BM_SquareRoot(Num: TBigNumber): TBigNumber;
Function BM_XRoot(Num, Exp: TBigNumber): TBigNumber;

//Compare Funktionen nach CPU-Flag-Notation
Function BM_CompareC(Num1, Num2: TBigNumber): Boolean; //Returns True if Num1<Num2
Function BM_CompareNC(Num1, Num2: TBigNumber): Boolean; //Returns True if Num1>=Num2
Function BM_CompareNZ(Num1, Num2: TBigNumber): Boolean; //Returns True if Num1<>Num2
Function BM_CompareZ(Num1, Num2: TBigNumber): Boolean; //Returns True if Num1=Num2

//Compare-Funktionen nach Arithmetischer Notation
Function BM_CompareE(Num1, Num2: TBigNumber): Boolean; //Returns True if Num1=Num2
Function BM_CompareNE(Num1, Num2: TBigNumber): Boolean; //Returns True if Num1<>Num2
Function BM_CompareL(Num1, Num2: TBigNumber): Boolean; //Returns True if Num1<Num2
Function BM_CompareLE(Num1, Num2: TBigNumber): Boolean; //Returns True if Num1<=Num2
Function BM_CompareG(Num1, Num2: TBigNumber): Boolean; //Returns True if Num1>Num2
Function BM_CompareGE(Num1, Num2: TBigNumber): Boolean; //Returns True if Num1>=Num2

//ggT und kgV
Function BM_GCD(Num1, Num2: TBigNumber): TBigNumber;
Function BM_LCM(Num1, Num2: TBigNumber): TBigNumber;

Procedure BM_Assign(Var Result: TBigNumber; Const Number: TBigNumber);
Procedure BM_Optimize(Var Result: TBigNumber);
Procedure BM_GCDOptimize(Var Num1, Num2: TBigNumber);

Function BMD_StrToBigNum(Str: String; HexInput: Boolean): TBigNumber;
Function BMD_BigNumToStr(Num: TBigNumber; HexOutput: Boolean): String;
Function BMD_FractionToStr(Num1, Num2: TBigNumber): String;


Was ist geplant?


Zugabe: BibNum_Lib
BigNum_Lib ist eine NOCH ÄLTERE Version einer Großzahl-Bibliothek von mir, die damals noch vollständig auf OOP-Basis geschrieben wurde. Diese hat sowohl ihre Vorzüge, als auch eine ganze Reihe an Nachteilen. Da ich festgestellt hab, dass in BigNum2 noch einige Funktionen (gerade in Bezug auf Primzahl-Arithmetik) fehlen, die bereits in BigNum_Lib enthalten waren, hänge ich beide Units an. Diese sind jedoch ausdrücklich source- und binary-inkompatibel. Ein Mischen beider Units ist daher nicht möglich.

Lizenz (Gilt für beide Dateien):


Feedback:
Wie immer gern gesehen!


g1o2k4 - Sa 17.05.08 18:59

super. vielen dank !
kanns nur weiterempfehlen !


Motzi - Sa 17.05.08 19:18

Frage: wieso verwendest du ein array of Byte und nicht ein array of Word? Das Ergebnis von Addition/Multiplikation zweiter 16-Bit Words kann ein 32-Bit DWord sowieso nicht überschreiten, aber du würdest nur mehr die halbe Anzahl an Rechenoperationen benötigen...


BenBE - Sa 17.05.08 19:30

@user profile icong1o2k4: Das hört man doch gerne. Bin über jegliche Anregungen, Verbesserungsvorschläge und Patches wie immer höchst erfreut.

@user profile iconMotzi: Das wäre zu überlegen, zumal es ja theoretisch sogar die Möglichkeit gäbe, mit DWORD zu rechnen und den 64-Bit-Datentyp der CPU (mit ein wenig ASM) herzunehmen. Muss ich mir mal in Ruhe anschauen, aber da ich derzeit noch einige Projekte offen hab, wird das sicherlich noch etwas Zeit in Anspruch nehmen, bis ich da dazu komme.


ub60 - Sa 17.05.08 19:55

Irgendwie waren noch 3 Copy&Paste-Fehler drin. So sollte es gehen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
Function BM_PowerMod(Base, Exp, Module: TBigNumber): TBigNumber;
Const
    C: Array[0..7Of Byte = ($01$02$04$08$10$20$40$80);
Var
    i, j: Integer;
Begin
    Result := BMD_StrToBigNum('1', false);
    For i := High(ExpDownto 0 Do
    Begin
        For j := 7 Downto 0 Do
        Begin
            Result := BM_Multiply(Result, Result);
            If (Exp[i] And C[j]) > 0 Then
                Result := BM_Multiply(Result, Base);
            Result := BM_Modulo(Result, Module);
        End;
    End;
End;


Ansonsten eine super Sache!

ub60

PS: Exp halte ich für keinen so sinnvollen Variablennamen. :wink:


BenBE - Sa 17.05.08 20:33

@user profile iconub60: Ansichtssache. Als Exponent ist es zumindest aussagekräftiger als X.

So, hab mal eine neue Version hochgeladen. Download wie immer im ersten Beitrag.


ub60 - Sa 17.05.08 20:44

user profile iconBenBE hat folgendes geschrieben:
@user profile iconub60: Ansichtssache. Als Exponent ist es zumindest aussagekräftiger als X.

Da stimme ich Dir zu. Ich meinte mehr die gleichnamige Exp-Funktion.
Ich hätte auch mit dem Namen "Exponent" leben können. :D

ub60


BenBE - Sa 17.05.08 20:47

user profile iconub60 hat folgendes geschrieben:
user profile iconBenBE hat folgendes geschrieben:
@user profile iconub60: Ansichtssache. Als Exponent ist es zumindest aussagekräftiger als X.

Da stimme ich Dir zu. Ich meinte mehr die gleichnamige Exp-Funktion.
Ich hätte auch mit dem Namen "Exponent" leben können. :D

ub60

Ich weiß, dass Du die meintest, dann hätte ich das aber in der halben Unit ändern müssen :P


Motzi - So 18.05.08 12:00

user profile iconBenBE hat folgendes geschrieben:
@user profile iconMotzi: Das wäre zu überlegen, zumal es ja theoretisch sogar die Möglichkeit gäbe, mit DWORD zu rechnen und den 64-Bit-Datentyp der CPU (mit ein wenig ASM) herzunehmen. Muss ich mir mal in Ruhe anschauen, aber da ich derzeit noch einige Projekte offen hab, wird das sicherlich noch etwas Zeit in Anspruch nehmen, bis ich da dazu komme.

Das ginge natürlich auch, allerdings müssten dann natürlich auch noch einige andere Stellen angepasst werden. Bei der Erweiterung von Byte auf Word müsste fast nichts geändert werden.

Gruß, Motzi


Horst_H - So 18.05.08 14:19

Hallo,

was soll dieses divmodcar von mir in dem Code, wo es nicht benutzt wird?

Ich empfehle mal http://www.submanifold.be/triade/GInt/gint.html anzuschauen, welches mit longword/cardinal arbeitet aber nur 31 von 32 bit nutzt, damit kein Uebertrag beim Addieren auftritt.
http://www.efg2.com/Lab/Library/Delphi/MathFunctions/Cryptography.htm#MultiplePrecision und dort angegeben
http://numbers.computation.free.fr/Constants/subjects.html#Algorithms ist auch eine schoene Seite fuer fft Multilplikation etc.

Mit freepascal und delphi kann man doch auch gmp nutzen
http://shootout.alioth.debian.org/debian/benchmark.php?test=pidigits&lang=all
oder NX-numerics http://www.ellipsa.net/public/nx/nx.html mit Assembler in den Grundfunktionen

Gruss Horst
P.S.
Hagen Redmann hat ja leider keine sourcen dabei.


Motzi - So 18.05.08 19:10

user profile iconHorst_H hat folgendes geschrieben:
Ich empfehle mal http://www.submanifold.be/triade/GInt/gint.html anzuschauen, welches mit longword/cardinal arbeitet aber nur 31 von 32 bit nutzt, damit kein Uebertrag beim Addieren auftritt.

@BenBE: solltest du auch auf DWords umstellen würd ich das aber nicht machen - ich würde die vollen 32-Bit ausnutzen und stattdessen die conditional move/conditional set ASM-Befehle verwenden um das Overflow Flag auszuwerten und den Übertrag zu addieren. Dadurch erspart man sich einen conditional jump und somit potenzielle branch misspredictions.
64-Bit Datentypen brauchst du dann nur beim Multiplizieren.

Schade nur, dass Delphi keine Intrinsics unterstützt...

Wegen der Optimierung beim Potenzieren kriegst du noch ne PN.

Gruß, Motzi


Horst_H - So 18.05.08 21:19

Hallo,

warum nicht dann gleich http://www.ellipsa.net/public/nx/nx.html nutzen das wahlweise massiv Assemblerroutinen (nx_kernel.pas) nutzt .
Wozu alles neu erinden. Man müsste nur einen Vergleich zu GMP,BigNum2 oder Hagen Redmann DEC_5_1c [http://www.michael-puff.de/Developer/Delphi/Importe/Hagen_Reddmann/] haben.

Gruß Horst


ub60 - So 18.05.08 21:41

user profile iconHorst_H hat folgendes geschrieben:
Hallo,

warum nicht dann gleich http://www.ellipsa.net/public/nx/nx.html nutzen das wahlweise massiv Assemblerroutinen (nx_kernel.pas) nutzt .

NX funzt nicht mit älteren Delphi-Versionen.
Mein D7 (und das liebe ich :wink: ) schmettert DX leider ab.

ub60


g1o2k4 - Mo 02.06.08 15:22

@BenBe: sag mal...haste das eigentlich auch zufälligerweise als c-quelltext ?


BenBE - Mo 02.06.08 15:35

Nö, aber in C als Semester-Beleg mit nem Kumpel schon mal implementiert ... Am Ende 20 Seiten nur Doku der Funktionsköpfe und insgesamt ~10kLOC an Source (Doku zählt da auch mit rein, weil die geTeXt war :mrgreen:). Die Lib hatte aber noch ein paar Probleme bei manchen Rechenoperationen, Hatte aber nen vollwertigen RPN-Parser drin ;-)


Allesquarks - Di 03.06.08 16:04

Wie Motzi schon sagte braucht man sich gar nicht auf 31 Bit beschränken, da die Überläufe im asm ja eh in flags kommen. Allerdings macht es bei Bigints wohl keinen Sinn alle dword signed zu halten, weshalb er wohl eher das carry flag meinte. Da braucht man dann gar keine conditional moves, sondern einfach adc (add with carry), wenn man darauf achtet, das Flag in der Schleife nicht zu verändern.
Ich habe das auf diesem Wege in assembler schon programmiert und hier ins Forum gestellt. Je nach deiner Formatierung (little oder Big-Endian) könnte meine Version sogar "binärkompatibel" sein, dann könntest du das einfach rüberkopieren. Insbesondere habe ich auch eine Funktion implementiert, die das dezimal ein- und ausgeben kann, welche (nach kurzem Überfliegen) glaube ich bei dir fehlt.

Ansonsten kann ich nur sagen Respekt: Ich bin bis heute noch nicht dazu gekommen mit den Grundrechenarten über Taylorreihen die komplexeren Funktionen zu implementieren. Mich würde aber brennend interessieren, was deine Wurzelfunktionen überhaupt zurückgeben (wenn ich das richtig gesehen habe sind das doch auch ganze Zahlen).


BenBE - Di 03.06.08 16:16

user profile iconAllesquarks hat folgendes geschrieben:
Wie Motzi schon sagte braucht man sich gar nicht auf 31 Bit beschränken, da die Überläufe im asm ja eh in flags kommen. Allerdings macht es bei Bigints wohl keinen Sinn alle dword signed zu halten, weshalb er wohl eher das carry flag meinte. Da braucht man dann gar keine conditional moves, sondern einfach adc (add with carry), wenn man darauf achtet, das Flag in der Schleife nicht zu verändern.
Ich habe das auf diesem Wege in assembler schon programmiert und hier ins Forum gestellt. Je nach deiner Formatierung (little oder Big-Endian) könnte meine Version sogar "binärkompatibel" sein, dann könntest du das einfach rüberkopieren. Insbesondere habe ich auch eine Funktion implementiert, die das dezimal ein- und ausgeben kann, welche (nach kurzem Überfliegen) glaube ich bei dir fehlt.

Zum Zahlenformat: Die Zahlen liegen Little Endian im Speicher, d.h. Index 0 liefert das Least Significant Digit (Ich kürz mal absichtlich nicht ab :mrgreen:).

Zum Konvertieren nach Dezimal. Das hat die BigNum_Lib mit drin, jedoch ist es dort, da es mit DivMod implementiert ist, sehr lahm. Wenn Du da was schnelleres hast, würd ich mich freuen.

user profile iconAllesquarks hat folgendes geschrieben:
Ansonsten kann ich nur sagen Respekt: Ich bin bis heute noch nicht dazu gekommen mit den Grundrechenarten über Taylorreihen die komplexeren Funktionen zu implementieren. Mich würde aber brennend interessieren, was deine Wurzelfunktionen überhaupt zurückgeben (wenn ich das richtig gesehen habe sind das doch auch ganze Zahlen).

Jup. Das ist der ganzzahlige Anteil, der ABGERUNDETEN eigentlichen Wurzel. D.h. Wurzel von 2 gibt er 1 zurück, bei Wurzel 3 gibt er auch 1 und bei Wurzel 4 gibt er 2 zurück. Ist über Tangenten-Verfahren\Regula Falsi implementiert.

Die Implementierung der trigonometrischen Funktionen wie Sin und Cos ist mit den Taylor-Reihen ein Krampf (ach wenn es geht). Man sollte damit aber nicht ernsthaft arbeiten; grad wenn man sehr große Winkel brauch, da man allein durch die BErechnung von Pi (was man für die Quadranten-Beziehungen benötigt) sagenhafte Ungenauigkeiten reinbekommt.


Allesquarks - Di 03.06.08 16:33

Hm bin halt kein Informatiker. Das zeigt sich auch daran, dass ich eine bigint-Multiplikation geschrieben habe und irgendwann ich glaube von Horst H aufgeschnappt habe, dass es eben auch n*log n Laufzeiten gibt.
Ich weiß jetzt nicht genau, wie das beim Sinus etc. ist, welche Algorithmen da besser sind als die Taylorreihen (für Anregungen wäre ich dankbar), allerdings fand ich es bei den Taylors immer gut mit dem Restglied, da ich immer nach einer definerten Genauigkeit aufhören wollte.
Planst du noch eine Implementierung von Fließkommazahlen? Falls ja würde es mich interessieren, wie du das angehen würdest, dass man ja bei irrationalen Zahlen irgendwann abbrechen muss.

Wegen meiner to dezimal Routine:
TBigint=binäre große Zahl
TCustomganzzahl=array of byte (Ziffernfolge mit Basis zwischen 0 und 255)

Meine biginttostring (TBigint to string) baut sich sukzessive die Zweierpotenzen im Zehnersystem (TCustomganzzahl) und addiert diese anhand der durch die binäre Zahl vorgegebenen Bitmaske. Ich benutze also die spezielle Funktion mul2 sowie Addition von Bignums (bei mir TCustomganzzahl) im Nichtbinärsystem.
Das ganze sind bei mir Klassen weshalb für jede Zahl dieser "Rainbowtable" ein Construktor aufgerufen wird. Ist aber nur zur besseren Übersichtlichkeit. Würde genausogut prozedural funktionieren.


Motzi - Di 03.06.08 19:02

user profile iconAllesquarks hat folgendes geschrieben:
Wie Motzi schon sagte braucht man sich gar nicht auf 31 Bit beschränken, da die Überläufe im asm ja eh in flags kommen. Allerdings macht es bei Bigints wohl keinen Sinn alle dword signed zu halten, weshalb er wohl eher das carry flag meinte. Da braucht man dann gar keine conditional moves, sondern einfach adc (add with carry), wenn man darauf achtet, das Flag in der Schleife nicht zu verändern.

Stimmt, ich meinte eigentlich das carry flag.. ADC hab ich komplett vergessen, damit geht es natürlich noch einfacher und effizienter.. wo gibts denn deinen Code? Würd gern mal einen Blick darauf werfen...


Allesquarks - Di 03.06.08 19:49

Ich hab mal probiert das prozedural auszukoppeln und in Open-Source Units gepostet unter Bigints (soviel steht da von mir auch nicht drin sollte schnell zu finden sein). Besser ist es aber eine neuere Version aus dem Source von meinem Taschenrechner RAFX zu entnehmen. Die entsprechenden Units sind rmath, TBigintzahlimpl, TBigfloatzahlimpl, TCustomganzzahlimpl und TCustomfloatzahlimpl. Sollte nicht so schwer zu finden sein da drin, da das meiste bisher nur aus den Bodys besteht. Der Quelltext insbesondere das asm sollten also auffallen.


wirbeldelphi - Di 22.07.08 19:12

Ich hoffe ich darf das als Newbee und als ersten Beitrag hier im Forum.. ;)

Feine Sache deine Unit (gratuliere!), aber es sind zwei recht offensichtliche Fehler drin.
Auf der Suche warum ein falsches Ergebnis bei

(12321^3) mod 873 => 297 ooops..

herauskommt ist dann die Ursache schnell klar:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
Procedure TSJBigNumber.Power(Exp: TBigNumber);
Begin
//    Self.fNumber := BM_Multiply(Self.fNumber, Exp);
    Self.fNumber := BM_Power(Self.fNumber, Exp);
End;

Procedure TSJBigNumber.Power(Exp: TSJBigNumber);
Begin
//    Self.fNumber := BM_Multiply(Self.fNumber, Exp.Number);
    Self.fNumber := BM_Power(Self.fNumber, Exp.Number);
End;


Du hast dort BM_Multiply anstelle von BM_Power verwendet. Nach der Änderung kommt auch das erwartete Ergebnis:

(12321^3) mod 873 = 397 :-)

Moderiert von user profile iconNarses: Code- durch Delphi-Tags ersetzt


BenBE - Mi 23.07.08 12:22

user profile iconwirbeldelphi hat folgendes geschrieben:
Ich hoffe ich darf das als Newbee und als ersten Beitrag hier im Forum.. ;)

Hält dich keiner auf ;-)

user profile iconwirbeldelphi hat folgendes geschrieben:
Feine Sache deine Unit (gratuliere!),

Danke

user profile iconwirbeldelphi hat folgendes geschrieben:
aber es sind zwei recht offensichtliche Fehler drin.
Auf der Suche warum ein falsches Ergebnis bei

(12321^3) mod 873 => 297 ooops..

herauskommt ist dann die Ursache schnell klar:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
Procedure TSJBigNumber.Power(Exp: TBigNumber);
Begin
//    Self.fNumber := BM_Multiply(Self.fNumber, Exp);
    Self.fNumber := BM_Power(Self.fNumber, Exp);
End;

Procedure TSJBigNumber.Power(Exp: TSJBigNumber);
Begin
//    Self.fNumber := BM_Multiply(Self.fNumber, Exp.Number);
    Self.fNumber := BM_Power(Self.fNumber, Exp.Number);
End;


Du hast dort BM_Multiply anstelle von BM_Power verwendet.

Für die Fehler war ich nicht zuständig ;-) Die gesamte TSJBignum-Klasse wurde mir als Zuarbeit für die Unit gegeben; hab ich also so übernommen ;-)

user profile iconwirbeldelphi hat folgendes geschrieben:
Nach der Änderung kommt auch das erwartete Ergebnis:

(12321^3) mod 873 = 397 :-)

Änder ich ab. Thx für den Hinweis.

Moderiert von user profile iconNarses: Code- durch Delphi-Tags ersetzt


wirbeldelphi - Mi 23.07.08 15:05

Ich wollte noch schaun, ob der karatsuba Algorithmus zum Multiplizieren fixer ist, aber die Geschwindigkeit ist eigentlich eh kaum messbar, BM_Multiply ist sehr fix.
Vielleicht magst du das noch einfügen als zweite Multiplikation.

Siehe auch http://de.wikipedia.org/wiki/Karatsuba-Algorithmus


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:
{a fast multiplying algorithm for huge positive numbers by karatsuba.  }
Function BM_MultiplyKaratsuba(Num1, Num2: TBigNumber): TBigNumber;
Var i, n2, n:Integer;
    Num1_high, Num1_low, Num2_high, Num2_low:TBigNumber;
    P1, P2, P3:TBigNumber;
Begin
   if Length(Num1) > Length(Num2) then
      n2 := Length(Num1)
   else
      n2 := Length(Num2);
   if odd(n2) then inc(n2);
   if n2 < 9 then  //short numbers by simple multiplication
      begin
      Result := BM_Multiply(Num1, Num2);
      exit;
      end;
   n := n2 div 2;

   SetLength(Num1_low,n); SetLength(Num2_low,n);
   for i:=0 to n -1 do
     begin
     if i < Length(Num1) then Num1_low[i]:=Num1[i] else Num1_low[i] := 0;
     if i < Length(Num2) then Num2_low[i]:=Num2[i] else Num2_low[i] := 0;
     end;
   SetLength(Num1_high,n); SetLength(Num2_high,n);
   for i:=n to n2 - 1 do
     begin
     if i < Length(Num1) then Num1_high[i - n]:=Num1[i] else Num1_high[i - n] := 0;
     if i < Length(Num2) then Num2_high[i - n]:=Num2[i] else Num2_high[i - n] := 0;
     end;
   P1 := BM_MultiplyKaratsuba(Num1_high, Num2_high); 
   P2 := BM_MultiplyKaratsuba(Num1_low, Num2_low);
   P3 := BM_MultiplyKaratsuba(BM_Add(Num1_high,Num1_low), BM_Add(Num2_high,Num2_low));
   SetLength(Result, n2 * 2); FillChar(Result[0],n2 * 2,0);
   for i:=0 to Length(P1)-1 do Result[i+n2] := P1[i];      // Result == P1 * base^2n
   Result := BM_Add(Result, P2);                           // Result == P1 * base^2n + P2
   P3 := BM_Sub(P3,P1);
   P3 := BM_Sub(P3,P2);                                    // P3 - P1 - P2
   Setlength(P1, n2 * 2); FillChar(P1[0],n2 * 2,0);        // reusing P1 as tmp variable
   for i:=0 to Length(P3)-1 do P1[i + n] := P3[i];         // (P3 -P1 -P2) * base^n
   Result := BM_Add(Result, P1);                           // Result == P1 * base^2n + P2 + (P3 -P1 -P2) * base^n
   BM_Optimize(Result);
End;


Moderiert von user profile iconNarses: Code- durch Delphi-Tags ersetzt


Martok - Mi 23.07.08 20:17

Ich hab auch noch 2 kleine Sachen:

BMD_BigNumToStr gibt einen Leerstring zurück, wenn die Zahl 0 darstellt. Sollte man evtl. Abfangen ;)

Der Kommentar bei BM_CompareZ stimmt IMHO nicht: //Returns True if Num1>=Num2 sollte eher //Returns True if Num1==Num2 sein. Zudem wüsste ich ganz gern mal für was Z nur NC eigentlich steht ;)


BenBE - Fr 25.07.08 04:33

user profile iconMartok hat folgendes geschrieben:
Ich hab auch noch 2 kleine Sachen:

BMD_BigNumToStr gibt einen Leerstring zurück, wenn die Zahl 0 darstellt. Sollte man evtl. Abfangen ;)

Jep, kümmer ich mich drum ;-)

user profile iconMartok hat folgendes geschrieben:
Der Kommentar bei BM_CompareZ stimmt IMHO nicht: //Returns True if Num1>=Num2 sollte eher //Returns True if Num1==Num2 sein. Zudem wüsste ich ganz gern mal für was Z nur NC eigentlich steht ;)

Das ist doch aber ganz einfach erklärt:
Z = Zero, NC = Not Carry. Sollte doch aber für jemanden, der schon mal ASM gesehen hat, nahezu offensichtlich sein, grad wenn's weitere Methoden C, NZ auch noch gibt, die zudem noch von Funktionen mit GT, LT, LE, GE, E und NE aufgerufen werden :mrgreen: Also Martok, das sieht man doch :mahn:


n-regen - Fr 01.08.08 12:10

Die folgende Funktion können - bis die Zuweisung in der Komponente implemetiert wird - wahrscheinlich einige brauchen:

Delphi-Quelltext
1:
2:
3:
4:
function IntToBigNum(Int: Integer):TBigNumber;
begin
 result := BMD_StrToBigNum(inttostr(Int), false);
end;


BenBE - Fr 01.08.08 13:06

Dafür gibt's ne schnellere Variante; implementier ich aber sobald ich das eh alles hier im Thread einarbeite ...


wirbeldelphi - Fr 01.08.08 20:58

Ich bastele grad an einer Unit namens BigNumSigned, die BugNum2 nutzt, aber damit vorzeichenbehaftet rechnet. Also nicht nur positive Zahlen, sondern auch negative.

Bist du interessiert so etwas einzuarbeiten? Bis jetzt habe ich


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:
Function  BM_Add(Num1, Num2: TSignedBigNumber): TSignedBigNumber;
Function  BM_Sub(Num1, Num2: TSignedBigNumber): TSignedBigNumber;
Function  BM_Divide(Num1, Num2: TSignedBigNumber): TSignedBigNumber;
Function  BM_Multiply(Num1, Num2: TSignedBigNumber): TSignedBigNumber;
Function  BM_Power(Base, Exp: TSignedBigNumber): TSignedBigNumber;

Function  BM_Abs(Num: TSignedBigNumber): TSignedBigNumber;
Function  BM_Div(Num1, Num2: TSignedBigNumber): TSignedBigNumber;
Function  BM_Mod(Num1, Num2: TSignedBigNumber): TSignedBigNumber;
Function  BM_Even(Num: TSignedBigNumber):boolean;
Function  BM_Odd(Num: TSignedBigNumber):boolean;

Function  BM_CompareC(Num1, Num2: TSignedBigNumber): Boolean;  //Returns True if Num1<Num2
Function  BM_CompareNC(Num1, Num2: TSignedBigNumber): Boolean; //Returns True if Num1>=Num2
Function  BM_CompareNZ(Num1, Num2: TSignedBigNumber): Boolean; //Returns True if Num1<>Num2
Function  BM_CompareZ(Num1, Num2: TSignedBigNumber): Boolean;  //Returns True if Num1=Num2
Function  BM_CompareE(Num1, Num2: TSignedBigNumber): Boolean;  //Returns True if Num1=Num2
Function  BM_CompareNE(Num1, Num2: TSignedBigNumber): Boolean; //Returns True if Num1<>Num2
Function  BM_CompareL(Num1, Num2: TSignedBigNumber): Boolean;  //Returns True if Num1<Num2
Function  BM_CompareLE(Num1, Num2: TSignedBigNumber): Boolean; //Returns True if Num1<=Num2
Function  BM_CompareG(Num1, Num2: TSignedBigNumber): Boolean;  //Returns True if Num1>Num2
Function  BM_CompareGE(Num1, Num2: TSignedBigNumber): Boolean; //Returns True if Num1>=Num2

Procedure BM_Assign(Var Result: TSignedBigNumber; Const Number: int64);            Overload//careful, only –2^63..2^63–1
Procedure BM_Assign(Var Result: TSignedBigNumber; Const Number: TBigNumber);       Overload;
Procedure BM_Assign(Var Result: TSignedBigNumber; Const Number: TSignedBigNumber); Overload;

Function  BMD_StrToSignedBigNum(Str: String; HexInput: Boolean): TSignedBigNumber;
Function  BMD_SignedBigNumToStr(Num: TSignedBigNumber; HexOutput: Boolean): String;

{ implementation of extended euclidian algorithm: GCD_ext }
Function  GCD_ext(a, b: TSignedBigNumber; var u,v: TSignedBigNumber): TSignedBigNumber;  { Result = GCD(a,b) and also GCD(a,b) = u*a + v*b }
Function  phi(p,q:TBigNumber):TBigNumber;                                                { Result = (p-1)*(q-1) }
Function  NextPrime(Num, Samples:TSignedBigNumber):TSignedBigNumber;                     { Samples determines how securly a prime is returned. Result => Num if Num is prime ; next prime otherwise }
Function  MillerRabin(Num, NumSamples: TSignedBigNumber):boolean;                        { checks Num to be prime; see comment at implementation }
Function  BM_Random(minimum, maximum:TBigNumber):TBigNumber;                             { generate random number }


Martok - Sa 02.08.08 03:22

user profile iconBenBE hat folgendes geschrieben:

user profile iconMartok hat folgendes geschrieben:
Der Kommentar bei BM_CompareZ stimmt IMHO nicht: //Returns True if Num1>=Num2 sollte eher //Returns True if Num1==Num2 sein. Zudem wüsste ich ganz gern mal für was Z nur NC eigentlich steht ;)

Das ist doch aber ganz einfach erklärt:
Z = Zero, NC = Not Carry. Sollte doch aber für jemanden, der schon mal ASM gesehen hat, nahezu offensichtlich sein, grad wenn's weitere Methoden C, NZ auch noch gibt, die zudem noch von Funktionen mit GT, LT, LE, GE, E und NE aufgerufen werden :mrgreen: Also Martok, das sieht man doch :mahn:

Was mich zu der Frage bringt: warum ist die Version die ich kommentiert hab eine andere als die im Source-Verzeichnis liegt?
Oh, falscher Suchpfad. Verdammt, ich compiliere die ganze Zeit gegen ne falsche Lib :autsch:

Ein Compare das wie von der Standard-Quicksort-Funktion verlangt -1/0/1/ zurück gibt wäre übrigens mal noch praktisch.


Flamefire - So 06.12.09 22:28

So.
Mal von mir ein Update der BigNum2 Unit.

Changes:
-Unterstützung negativer Zahlen
-Implementierung Compare funktion nach Standart (-1;0;1)
-Umwandlung in/von Integer,Int64,Cardinal


Gammatester - Mo 07.12.09 00:06

Da sind aber noch einige Bugs drin, hier ein paar zur Ansicht: BM_modulo(12345,5)=5 und noch schlimmer für TSJBigNumber: modulo(-12345,-5)=-5 und viel schlimmer modulo(-5,-12345)=-12350.

Weiter: a.assign(-12345); a.assign('7'); ergibt -7!

Gruß Gammatester


Flamefire - Mo 07.12.09 00:14

Ok, schonmal vielen Dank
den Assign-Bug habe ich behoben. War mir nicht aufgefallen, da ich den String nie mehr verwendet hab.
Den Modulo macht BenBE demnächst.


BenBE - Mo 07.12.09 00:19

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
Den Modulo macht BenBE demnächst.

Ich nehm gerne Patches entgegen, schau's mir aber mal die Tage mit an, ob ich's direkt finde, was er da hat ...

Danke aber schonmal für die Testcases.

Edit: Testcase 1 grad durchdebuggt (ohne den Wrapper):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm1.Button1Click(Sender: TObject);
var
  B1, B2, BR: TBigNumber;
begin
  B1 := BMD_StrToBigNum('12345', false);
  B2 := BMD_StrToBigNum('5', false);
  BR := BM_Modulo(B1, B2);
  Caption := BMD_BigNumToStr(BR, false);
end;

Liefert das korrekte Ergebnis 0.

Die Vorzeichenbehafteten können nicht an mir liegen, die hat Flamefire gebaut ...

Edit 2: @Flamefire: Bei Modulo muss die Berechnung an BM_Modulo ohne Beachtung der Vorzeichen gereicht werden. Das Ergebnisvorzeichen richtet sich dann nach dem XOR der Eingabe-Vorzeichen, unabhängig von dem, was BM_Modulo liefert (außer halt fSign=0).


Gammatester - Mo 07.12.09 00:36

Das mit BM_modulo(12345,5)=5 stimmt nicht (sorry, falsche Variable ausgegeben), BM_modulo arbeitet hier richtig. Die TSJBigNumber.modulo-Fehler bleiben.

Gruß Gammatester


BenBE - Mo 07.12.09 00:56

Jap, wie gesagt das betrifft die letzte Kondition dort in der Funktion, die muss raus. Und ferner muss die Vorzeichenbehandlung noch korrigiert werden.

Zwecks Vorzeichen ist mir in erinnerung:
+ mod + --> +
- mod + --> +
+ mod - --> -
- mod - mod -
(Also Vorzeichen des Divisors).
Vgl. http://de.wikipedia.org/wiki/Modulo#Beispiel_2 bzw. Implementierung in Computersystemen. Besonders auf die Konvention in der Mathe achten.


Flamefire - Mo 07.12.09 16:21

jo sry. mein ansatz war, die berechnung im restklassen ring...Also alle Module>=0
hab da aber in der eile, fertig zu werden, da was übersehn

Na gut dann halte ich mich an die Konventionen und es kommt dann das Vorzeichen des Divisors raus.
Update im Anhang:


BenBE - Mo 07.12.09 16:44

Ich warte hier noch kurz die Rückmeldung bzgl. Funktionalität ab und würde die aktualisierte Version dann oben im ersten Beitrag (nach etwas Code-Formatter-Kosmetik) veröffentlichen.


Flamefire - Mo 07.12.09 16:51

Die Grundrechenarten (+,-,*,/) sind im Extrem getestet (weißt ja wo ;-) )
Ich gehe also davon aus, dass die funktionieren.
Umwandlungen hab ich nochmal überprüft, kann ich zu 99% sagen, dass es ok ist.

Mod, Log, Wurzel habe ich nicht geprüft, da es schon fast zu trivial ist. Es werden lediglich die Vorzeichenkonventionen eingehalten (x^-y=0 per Definition, da es Integer sind außer bei x=1 oder x=0)
Beim Mod hatte ich mich vertan, ok...

Auch GGT und KGV sind ungetestet. Hab mich da aber strikt an Wikipedia gehalten.

Und wegen Formatierung: Bei dir sind 2 Tabs drin. Bei mir nur einer. Und sonst...Da hat glaube jeder seine vorlieben, wo er begin und else usw. platziert. Ich finde es so übersichtlicher, da der Code damit nicht so lang wird.


spawn89 - Mo 07.12.09 17:09

Ein Unit-test wäre aber auch einfach zu schön für das Teil, nicht? :wink: :D

Edit:
Wenn ich zuhause bin könnt ich ja mal meinen drüberlaufen lassen. ^^


BenBE - Mo 07.12.09 17:13

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
Die Grundrechenarten (+,-,*,/) sind im Extrem getestet (weißt ja wo ;-) )

Jap. ;-)

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
Ich gehe also davon aus, dass die funktionieren.

Wie das halt immer so ist ... mit Theorie und Praxis.

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
Umwandlungen hab ich nochmal überprüft, kann ich zu 99% sagen, dass es ok ist.

Ich schau dann eh noch mal drüber.

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
Mod, Log, Wurzel habe ich nicht geprüft, da es schon fast zu trivial ist. Es werden lediglich die Vorzeichenkonventionen eingehalten (x^-y=0 per Definition, da es Integer sind außer bei x=1 oder x=0)
Beim Mod hatte ich mich vertan, ok...

Ich hoffe, für 0^-1 gibt's ne Exception ;-)

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
Auch GGT und KGV sind ungetestet. Hab mich da aber strikt an Wikipedia gehalten.

Da sollte man sich auf jeden Fall noch mal die Vorzeichenproblematik angucken. Wobei ich ggT immer positiv, kgV positiv, außer beide negativ vorschlagen würde (sollte man ggf. noch mal Abgleichen mit Wiki oder richtiger Quelle).

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
Und wegen Formatierung: Bei dir sind 2 Tabs drin. Bei mir nur einer. Und sonst...Da hat glaube jeder seine vorlieben, wo er begin und else usw. platziert. Ich finde es so übersichtlicher, da der Code damit nicht so lang wird.

Hab nen Source-Formatter, der hat meinen Code-Stil gleich als Konfig drin. Da geht das ganz zügig mit formatieren.

Und gegen zu langen Code gibt's Code Folding :P

@user profile iconspawn89: Reiche mir nen kleines Demo-Projekt als Unit-Test ein, dann füg ich den bei. Ansonsten müsst ich mal meinen Großzahlen-Taschenrechner mit RPN-Parser raussuchen; der war Ausgangspunkt für die Unit.


Flamefire - Mo 07.12.09 17:44

ggt(-a,b)=ggt(a,b)
und ggt(a,b)=ggt(b,a)

-->ggt>=0 (wobei ich auch bei der 0 noch was hatte...glaube ich)

und kgv(a,b)=|a*b|/ggt(a,b)

-->auch immer >=0

und ja bei 0^-y gibts ne exception ;-)
und (-x)^y macht er auch korrekt

wurzeln hab ich generell nur von x>=0 definiert (zwecks (-8)^(1/3)=-2 wenn man so rechnen könnte, lt. wiki aber n.d.)


Gammatester - Mo 07.12.09 20:33

Hier noch ein paar schnelle Bugs und Features aus dem Gammatest (bevor der neue Source rausgeht):

1. (-10)^5 = 100000 statt richtig -100000

2. log(x,1) sollte exception werfen, gibt stackoverflow

3. Standard: gcd(0,0)=0, gibt exception

4. Standard: lcm(0,0)=0, gibt accces violation

5. Wie ist xroot(a,-b) definiert? = 1/xroot(a,b) ? Dann wäre xroot(2,-3) = 1/xroot(2,3) = 1/1 = 1!
Also entweder nicht zulassen oder =1 aber nicht = 0

6. sqr sollte sqrt heißen, bzw wirklich als sqr(a) = a*a implementiert werden

Gruß Gammatester


Flamefire - Mo 07.12.09 23:17

Danke fürs Testen.
1) War ein Tippfehler...Hab die Zahl auf ungerade geprüft, nicht den Exponent ^^
2) Ok ist mir entfallen. Stand nicht auf Wiki ;-)
3) Lt. Wiki ist ggt(0,0) nicht definiert. Hier [http://de.wikipedia.org/wiki/Gr%C3%B6%C3%9Fter_gemeinsamer_Teiler]
Weil:
c=ggt(a,b)
dann gilt u.a.:
a mod c=0 und b mod c=0
wenn ggt(0,0)=0 dann wäre 0/0=0 Rest 0
Aber x/0 ist nicht definiert.
4) Hab dort einfach an BenBEs funktion weitergegeben. Berechne es jetzt selbst. Sollte funktionieren.
5) root(2,-3)=0.7937...-->Auf Integer ist das 0
Aber ok...eigendlich müsste man es ja aufrunden und dürfte immer auf was >=0,5 kommen
6) Ist geändert und eine sqr funktion mit eingebaut.


BenBE - Mo 07.12.09 23:58

@4. Was spricht gegen ggT(0,0)=1 ??? Aber gut, wenn Wiki n.d. sagt, passt's so.


Gammatester - Di 08.12.09 00:28

Alle ernstzunehmenden mathematischen Quellen setzen gcd(0,a) = abs(a).
Leider ist Wiki (speziell das deutsche) oft genug keine solche ernstzunehmende Quelle.
ZB sagt es gcd(a,b) = gcd(b,a) und gcd(a,0)=|a|, weigert sich aber dann auch gcd(0,a)=|a| und gcd(0,0)=0 aufzulisten!
Das englische Wiki behauptet nicht, daß gcd(0,0) undefiniert sei, im Gegenteil: "It is useful to define gcd(0,0)=0"

Im übrigen rechnet auch bignum2 zB gcd(0,a)=abs(a), also sollte doch im Spezialfall a=0 dann auch gcd(0,0)=0 herauskommen.

Schönen Abend
Gammatester


BenBE - Di 08.12.09 00:58

gcd(0,0) kann man streiten, weil 1 ein Teiler von 0 ist (ich weiß, klingt doof, ist aber so) ... Aber ich kann mit der Definition der englischen Wiki (bzw. ernstzunehmender Literatur) durchaus leben. Zumal ich eh immer Source ohne Ausnahmeregeln bevorzuge (wenn's möglich ist).

Bei Mathe weiß man eh immer nicht, ob die unvollständig oder falsch ist :P


Flamefire - Di 08.12.09 11:55

ähm...
du sagst
Zitat:
gcd(a,b) = gcd(b,a) und gcd(a,0)=|a|

aber gcd(0,a)=|a| sei nicht aufgeführt?

ist es aber:
gcd(0,a)=gcd(a,0)=|a|

und in der wiki steht eindeutig das gcd(0,0) nicht definiert ist.

das problem ist das gleiche wie log zur basis 1:

Eine natürliche Zahl a teilt b wenn es ein c gibt mit: b=a*c
demzufolge würde gelten, dass für jedes x gilt: x|0. weil 0=x*0

da die natürlichen Zahlen unendlich sind, gibt es kein größtes x.


Gammatester - Di 08.12.09 15:33

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
ähm...
du sagst
Zitat:
gcd(a,b) = gcd(b,a) und gcd(a,0)=|a|

aber gcd(0,a)=|a| sei nicht aufgeführt?

ist es aber:
gcd(0,a)=gcd(a,0)=|a|
Wer lesen kann ist klar im Vorteil: auf der deutschen Wikiseite steht nur gcd(a,0)=|a|! Leider weigert sich der Autor unter Berücksichtigung der Symmetrie gcd(0,a) abzuleiten und dann den Speziallfall a=0!
Zitat:
und in der wiki steht eindeutig das gcd(0,0) nicht definiert ist.
Das ist ja genau das Problem.
Zitat:
das problem ist das gleiche wie log zur basis 1:
Nein! Ist es ganz und gar nicht: log(x,a) = ln(x)/ln(a) bedeutet log(x,1) = ln(x)/ln(1) = ln(x)/0 und das ist nun wirklich durch keine Definition zu retten!
Zitat:

Eine natürliche Zahl a teilt b wenn es ein c gibt mit: b=a*c
demzufolge würde gelten, dass für jedes x gilt: x|0. weil 0=x*0

da die natürlichen Zahlen unendlich sind, gibt es kein größtes x.
Es gibt viele Möglichkeiten den gcd zu definieren. Hier die mM sinnvollste für ganze Zahhlen: "gcd(a,b) ist der eindeutige nicht-negative Generator der von a und b erzeugten Untergruppe von Z", und die von a=b=0 erzeugte Untergruppe ist nun mal {0}, und wer erzeugt das? Analog läuft die idealtheoretische Definition (siehe u.a. engl. Wiki).

Wenn Du nicht glaubst, daß gcd(0,0)=0 ist, schau nach bei Knuth oder bei Maple, Pari usw, oder tippe gcd(0,0) bei Wolfram Alpha ein.

Wie gesagt, Wiki ist oft nicht zuverlässig und sollte durch andere Quellen bestätigt werden.


Flamefire - Di 08.12.09 17:08

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Wer lesen kann ist klar im Vorteil: auf der deutschen Wikiseite steht nur gcd(a,0)=|a|! Leider weigert sich der Autor unter Berücksichtigung der Symmetrie gcd(0,a) abzuleiten und dann den Speziallfall a=0!


ich wollte damit nur darauf aufmerksam machen, dass es aus den gegebenen regeln ableitbar ist.
sonst müsste z.b. auch gcd(-a,-b)=gcd(a,b) u.a. da stehen.
das zusätzlich aufzuschreiben ist wegen der Kommutativität nicht nötig.

Hab nochmal nachgeguckt: Mathematisch gesehen ist der ggt(0,0) nicht definiert, aufgrund meiner obigen Beweisführung
algebraisch (also z.b. nach deiner Defintion) sieht es anders aus. Steht so auch auf wiki.
Ehrlich gesagt weiß ich absolut nicht, wie man das Problem hier lösen soll.
Denn eigendlich rechnet man hier ja nur mathematisch, andrerseits gibt es auch algebraische Anwendungen.

Und nur als Anmerkung zum log:
log(1,1)=ln(1)/ln(1)=0/0=0?

Aber insgesammt, werde ich es jz im Code so notieren, dass gcd(0,0)=0 ist. Schient zumindest besser, als ein Fehler.
Lade den Code aber nicht neu hoch, da es nur eine Änderung ist. Solltest du noch merh finden, was nicht korrekt ist, fasse ich das dann zusammen, und lade das dann hoch.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TSJBigNumber.GCD(Num2: TSJBigNumber);
begin
  if(self.fSign=0and (Num2.Sign=0then begin
    //raise EMathError.Create('GCD of (0,0) is not defined');
    exit;//gcd(0,0):=0 (per algebraischer Definition)
  end;
  if(Num2.fSign<>0then begin
    if(self.fSign=0then self.Assign(Num2) //gcd(0,a)=|a|
    else self.fNumber:=BM_GCD(self.fNumber,Num2.Number);
  end;
  //else ggt(a,0)=|a|
  self.fSign:=1;
end;


Gammatester - Di 08.12.09 17:25

1 Bug, 2 Featurerequests:

Bug: IsOne(-1)=true

Featurerequests:
- IsZero public machen wie IsOne
- Self.Assign(int64(1)) oder cardinal(1) in XRoot und Power damit's auch Delphi6 kompatibel ist. Eine nackte 1 bringt den Fehler "Ambiguous overloaded call to 'Assign'"

Gruß Gammatester


Flamefire - Di 08.12.09 18:33

Geht klar.
IsOne sollte sich um den Absolutbetrag kümmern. Ist umbenannt und entsprechend Funktionen hinzugefügt.
IsZero war nicht vorgesehn, da stattdessen Num.Sign=0 geprüft werden kann.
Aber das ist jetzt auch mit drin.
Die Assigns sind gefixt.


Horst_H - Di 08.12.09 23:41

Hallo,

was sind das alles für schreckliche Sachen ;-)
Warum wird nicht mit wenigstens Word statt byte gearbeitet.
Oder dies hier:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
Function BM_Add(Num1, Num2: TBigNumber): TBigNumber;
Var
    X: Integer;
    O: Integer;
Begin
    SetLength(Result, Max(Length(Num1), Length(Num2)) + 1);
    FillChar(Result[0], Length(Result), 0);

    O := 0;
    For X := 0 To High(Result) Do
    Begin
        If X < Length(Num1) Then
            O := O + Num1[X];
        If X < Length(Num2) Then
            O := O + Num2[X];

        Result[X] := O And $FF;
        O := O And $FF00 Shr 8;
    End;
    BM_Optimize(Result);
End;


Wäre es nicht sinniger, Result erst mit mit der längeren der beiden Zahlen zu füllen, um eine Stelle verlängern, dann eine Schleife mit Übertragsrechnung und Summation der kürzeren Zahl mit result und anschliessend nur noch solange es einen Übertrag gibt?

Gruß Horst


Flamefire - Di 08.12.09 23:54

Dass das ganze nicht optimal ist, hatte BenBE schon geschrieben, da seine Unit eher aus Spaß entstanden ist.
Man kann sie benutzen und mit der Wrapper Klasse nun auch für alle x€Z (einzige Beschränkung ist freier Speicher)

Wenn die jemand optimieren möchte, dann gerne ;-)


dagri - Fr 17.12.10 19:05

Um das Niveau mal zu senken: Kann mir einer mal sagen, wie ich eine Integer Zahl in eine BigNum Zahl umwandele? Ich habe BigNum in der Schule leider noch nicht durchgenommen und daher keine Ahnung davon :(


BenBE - Sa 18.12.10 08:31


Delphi-Quelltext
1:
Function BMD_StrToBigNum(Str: String; HexInput: Boolean): TBigNumber;                    


Die Parameter sollten eigentlich eindeutig sein.

Ansonsten auf von Jaenicke die OOP-Erweiterung angucken; insbesondere TSJBigNum.Assign.


delfiphan - Sa 18.12.10 09:48

Bei einer solchen Unit mit so grossem Echo wären ein paar Duzend Unit-Tests sicher gut. Ich gehe mal davon aus, dass du die Unit irgendwie mit ein paar Beispielzahlen getestet hast, wieso nicht gleich richtige Unit-Tests und die gleich mitliefern?

Übrigens: Delphi unterstützt auch Operator Overloading [http://docwiki.embarcadero.com/RADStudio/de/%C3%9Cberladene_Operatoren]. Operator Overloading braucht man fast nie, ausser in genau diesem Fall :)


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var
  A, B: TBigInt;
begin
  A := '4000000000000000000001'// Implicit string conversion
  B := 5// Implicit integer conversion

  // Arbitrary math expression
  ShowMessage(IntToStr(A mod B + 2)); // 3

Die Stringkonvertierung sieht ein bisschen nach Magic aus, daher muss es über den Compilerswitch implicitstringconversion aktiviert werden.

// Edit: Da BigNum2 keine negativen Zahlen unterstützt, tut es das hier natürlich auch nicht.


BenBE - Mo 20.12.10 11:39

Die Unit ist zu Delphi-4-Zeiten entstanden, wo es das noch nicht gab. Bin auch nicht unbedingt erfreut, wenn die Nutzung neuer Features alten Code bricht, also bitte den Patch mit bedingte Compilierung vorschlagen.

Und die fehlenden Unit Tests: Hatte noch nicht einen Fall, wo sich dieser Aufwand gelohnt hätte.

Die Unit ist noch zu Gymnasialzeiten entstanden, daher auch kein Fokus auf Security oder Performance. Für Spielerin reicht das vielleicht, für richtige Anwendungen muss man sich den Kram eh für seinen Zweck anpassen.


delfiphan - Mo 20.12.10 13:02

Hab erst nach dem Post gemerkt, dass der Thread uralt ist :)

Alten Code bricht dieser Code nicht. Es handelt sich hier um eine zusätzliche Unit, die deine verwendet und einfach eine vereinfachte API bietet (Anwendung äquivalent zu "Integer").

Ich hatte schon angefangen Unit-Tests zu schreiben, als ich merkte, dass vieles failed. Erst dann ist mir klar geworden, dass negative Zahlen nicht unterstützt werden und ich dafür die neue Klasse von Jaenicke verwenden müsste. Das funktioniert aber schlecht mit meinem Record-Typ zusammen (da der Record-Typ nicht instanziert werden muss), daher hab ich es jetzt einfach sein lassen :\


Flamefire - Mo 20.12.10 14:38

unit-tests hätten hiefür schon mal was. weil sowas brauch man doch häufiger und das hier ist die einzige wirklich gute unit, die ich kenne/gefunden habe (damals als ich das brauchte ;) )
Ich hatte dafür auch die Erweiterung in die Klasse geschrieben, das negative Zahlen unterstützt werden. Ist aber eben nur in der Klasse enthalten. Operator-Überladen geht nur mit records? Oder doch auch mit Klassen? Wäre ja schon ziemlich cool, wenn man vl die Klasse als Record umschreiben und so wie normale Integer verwenden kann. Hatte ich ja damals das Problem:
Integer --> wurden zu klein, int64 auch zu klein, float typen wurden ungenau. also viel code umschreiben um die klasse zu verwenden, obwohls auch nur ne zahlendarstellung ist...
also sinnvoll wäre es.

vl mal ein statement von BenBE dazu (ist ja hauptsächlich seins)


delfiphan - Mo 20.12.10 15:17

Ich wär bereit, das umzuschreiben, aber nur, wenn die Lizenz dahingehend geändert wird, dass man die Library auch für kommerzielle Zwecke einsetzen darf (auch ohne separate Bewilligung), vorzugsweise eine bekannte Open-Source Lizenz. Meinetwegen mit entsprechendem Verweis in den Credits oder Doku. Ansonsten macht's für mich nicht viel Sinn, hier Arbeit reinzustecken. ;)


dagri - Di 21.12.10 11:41

ah, danke, hatte ich nicht gesehen. Nur was ist meine Boolean Zahl, also diese HexInput: Boolean? Also was für einen wert muss die am Anfang haben? Kann ich die einfach auf true setzen und dann die funktion aufrufen? Weil dann wär die ja sinnlos :-/


Yogu - Di 21.12.10 13:56

user profile icondagri hat folgendes geschrieben Zum zitierten Posting springen:
Nur was ist meine Boolean Zahl, also diese HexInput: Boolean?

Die gibt an, ob der Eingabestring Str die Dezimal- oder Hexadezimaldarstellung einer Zahl ist. Wenn du IntToStr verwendest, musst du den Parameter auf False setzen.


dagri - Fr 07.01.11 16:28

Kann man auch ein (dynamisches) Array x erstellen, welches die Größe einer BigNumber Zahl b hat, und dann das Array mit x[b] ansprechen? Hat bei mir leider nicht funktioniert und in der Unit habe ich dazu auch nichts gefunden.


Yogu - Fr 07.01.11 16:33

user profile icondagri hat folgendes geschrieben Zum zitierten Posting springen:
Kann man auch ein (dynamisches) Array x erstellen, welches die Größe einer BigNumber Zahl b hat, und dann das Array mit x[b] ansprechen? Hat bei mir leider nicht funktioniert und in der Unit habe ich dazu auch nichts gefunden.

Ich würde doch fast meinen, ein Array mit 92 23 372 036 854 775 807 Einträgen dürfte genügen, oder? Dann reicht nämlich auch ein 64-Bit-Integer als Typ für den Index.


BenBE - Sa 08.01.11 08:09

Dass Delphi Datentypen mit mehr als 2GB zulässt, wäre mir neu. Selbst bei 64 Bit macht das nur wenig Sinn. Und insbesondere array[Int64] of Byte würde als Index-Typ nur bedingt Sinn ergeben ;-)

Außerdem: 256^(2^32) sollten doch wohl für's erste reichen, oder? Ist immerhin 2^35 * ln(2)/ln(10) ... Also ne Zahl mit UNGEFÄHR 20 Milliarden Dezimalstellen...


dagri - Sa 08.01.11 14:56

user profile iconYogu hat folgendes geschrieben Zum zitierten Posting springen:
user profile icondagri hat folgendes geschrieben Zum zitierten Posting springen:
Kann man auch ein (dynamisches) Array x erstellen, welches die Größe einer BigNumber Zahl b hat, und dann das Array mit x[b] ansprechen? Hat bei mir leider nicht funktioniert und in der Unit habe ich dazu auch nichts gefunden.

Ich würde doch fast meinen, ein Array mit 92 23 372 036 854 775 807 Einträgen dürfte genügen, oder? Dann reicht nämlich auch ein 64-Bit-Integer als Typ für den Index.

Ich habe dies in meinem Programm ausprobiert, allerdings kommt die Fehlermeldung "Fehler bei Bereichsüberprüfung". Das bedeutet doch wohl, dass die Arraygröße bzw. a zu groß ist, oder?:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var a: Int64;
    x: Array of TBigNumber;
begin
  a := 4294967290;
  setLength(x, a);
end;


Die Fehlermeldung wird bei setLength gezeigt. Leider habe ich vergeblich im Internet nach einer Maximalgröße eines arrays oder nach dem typ der Variable, die die Größe des Arrays definiert, gesucht :(

Moderiert von user profile iconMartok: Delphi-Tags gesetzt


Yogu - Sa 08.01.11 22:18

user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
Dass Delphi Datentypen mit mehr als 2GB zulässt, wäre mir neu.

Stimmt, das hatte ich vergessen. Ich wollte nur zeigen, dass ein BigNum vollkommen sinnlos ist.

user profile icondagri hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe dies in meinem Programm ausprobiert, allerdings kommt die Fehlermeldung "Fehler bei Bereichsüberprüfung". Das bedeutet doch wohl, dass die Arraygröße bzw. a zu groß ist, oder?:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var a: Int64;
    x: Array of TBigNumber;
begin
  a := 4294967290;
  setLength(x, a);
end;

Delphi ist bei mir zwar schon eine Weile her, aber ich glaube, dass dynamische Arrays einen Integer als Index verwenden. Und dessen Maximalwert ist 2 147 483 647. Aber überlege doch mal - ein BigNum braucht im Leerlauf 4 Byte Speicher, also benötigt ein Array mit 2 147 483 647 BigNum-Elementen 8 GB. Und das am Stück. Mit tatsächlichen Zahlen sind es nochmal vieel mehr... Du musst doch einsehen, dass so etwas wahnsinnig übertrieben ist, oder?


Pussyranger - Di 09.08.11 10:50

Hallo,
ich möchte auch mit BigNum2 arbeiten, aber ich krieg's noch nicht mal hin zwei Zahlen zu addieren ^^

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TForm1.Button1Click(Sender: TObject);
VAR Zahl1,Zahl2: TBigNumber;
begin
  Zahl1:=-2;
  Zahl2:=0.05;
  Zahl1:=Multiply(Zahl1); //Zahl1 quadrieren
  Zah2:=Add(Zahl1);       //Zahl1+Zahl2
end;

Es scheitert bereits bei der Zuweisung 'Zahl1:=-2;'.

Was mache ich falsch?
LG,

Pussyranger

Moderiert von user profile iconNarses: Quote- durch Delphi-Tags ersetzt


Yogu - Di 09.08.11 13:02

Hallo user profile iconPussyranger,

eine BigNum kannst du über einen String erstellen:


Delphi-Quelltext
1:
Zahl1 := BMD_StrToBigNum('123');                    

Wobei ich glaube, dass negative Zahlen nicht möglich sind (oder?)

Und deine Operation Zahl1 := Multiply(Zahl1); kann ja gar nicht funktionieren: Du übergibst für die Multiplikation nur einen Parameter. Außerdem heißt die Funktion BM_Multiply.


Pussyranger - Di 09.08.11 18:39

Danke für die schnelle Antwort!
Leider kann ich mit der Lib weder mit negativen noch mit Kommazahlen rechnen, oder etwa doch?
Gibt es denn sonst noch eine andere Lib, die mit den oben genannten Zahlen umgehen kann? Ich benötige nur die Rechenoperationen +,-,*.


Gammatester - Mi 10.08.11 08:45

user profile iconPussyranger hat folgendes geschrieben Zum zitierten Posting springen:
Danke für die schnelle Antwort!
Leider kann ich mit der Lib weder mit negativen noch mit Kommazahlen rechnen, oder etwa doch?
Gibt es denn sonst noch eine andere Lib, die mit den oben genannten Zahlen umgehen kann? Ich benötige nur die Rechenoperationen +,-,*.

Ja solche Libs gibt es, zB die MPArith.Bibliothek [http://home.netsurf.de/wolfgang.ehrhardt/misc_de.html#mparith], die freie Opensource Pascal/Delphi-Quellcodes zur Verfügung stellt für Multipräzisions-Arithmetik für ganze, rationale und reelle Zahlen. Allerdings mußt Du Dich (wie auch schon bei BigNum) damit abfinden, daß ein wenig Eigenarbeit nötig ist, sprich: Man kann nicht einfach x := a*b mit großen Zahlen schreiben, sondern zB mpf_mul(a,b,x) für Fließkomma- oder mp_add(a,b,x) für Ganzahlen. Selbstverständlich werden negative Zahlen unterstützt.


Delphi-Laie - So 03.06.12 13:15

Hallo Benny / BenBE!

Erst einmal auch von meiner Seite ein großes Kompliment für diese Unit, auf deren Grundlage ich gerade ein Langzahlrechenprogramm schreibe.

Mir sind zwei Dinge aufgefallen:

1. Hierbei geht nur um einen Kommentar: Hinter der Funktion BM_DivMod steht "Returns Int as Num1, Mod as Result". Nach meiner Beobachtung und meinen Kenntnissen ist es genau umgekehrt: In Num1 wird der Modulo und als eigentliches Funktionsergebnis der Ganzzahlanteil zurückgeliefert.

2. Ernster ist folgendes Problem: Multipliziert man die Zahl 16 mit der Funktion BM_Multiply mit sich selbst, so springt im Ergebnis derselben das interne Format vom üblichen (BigNumber) auf (0,1). bei 32 auf (0,4), bei größeren Zweierpotenzquadraten auch auf (0,0...) usw., und das anscheinend bei allen folgenden Zweierpotenzen. Auch Multiplikationen einer Zweierpotenz mit 2 sind davon betroffen.

Die Rückkonvertierungsfunktion BMD_BigNumToStr läßt sich davon nicht beirren, doch für interne Weiterrechnungen ist dieser Formatwechsel leider tödlich.

Mir fiel das auf, als ich eine schnelle Potenzierungsfunktion (auf der Basis des Binärformates des Exponenten) implementieren wollte und bei den Zweierpotenzen zu falschen Ergebnissen kam.

Auch die Funktion BM_Power scheint leider davon betroffen zu sein.

Viele Grüße

Delphi-Laie


BenBE - So 03.06.12 13:49

Das interne Format von BigNums ist Little Endian auf Bytes. Von daher ist 16*16 = 0 * 256^0 + 1 * 256^1 korrekt. Für 32*32 = 0 * 256^0 + 4 * 256^1 stimmt das genauso. Wenn Du einmal die PowMod-Funktion anschaust, dann sollte die sogar schon auf die Art das Ergebnis rechnen.

Das andere scheint wirklich ne Implementationssache zu sein, weil in der Regel bei DivMod das Modulo-Ergebnis benötigt wird und das Div-Ergebnis in den nächsten Scheifendurchlauf einfließt.

Gruß,
BenBE.


Delphi-Laie - So 03.06.12 15:32

Hallo Benny, bitte entschuldige, es war natürlich doch mein Fehler (den ich jetzt fand)....bei Deiner ausgereiften Unit mußte es wohl so sein.

Was der dritte Parameter Deiner PowerMod-Funktion (module) für einen Zweck hat, ist unklar. Durch Experimente fand ich heraus, daß er, auf null gesetzt, zum gewünschten Ergebnis (Potenz) führt.

Kann es sein, daß Du intern mit dem Zahlensystem zur Basis 256 arbeitest? So kommt es mir immer wieder vor, wenn ich Deinen Quelltext an unterschiedlichsten Stellen zu analysieren mich bemühe.

Ergänzung: Der Geschwindigkeitszuwachs von BigNum_Lib zu BigNum2 ist phänomenal.


BenBE - Mo 04.06.12 17:57

user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Hallo Benny, bitte entschuldige, es war natürlich doch mein Fehler (den ich jetzt fand)....bei Deiner ausgereiften Unit mußte es wohl so sein.

Gab es da jemals Zweifel? :twisted: 8)

user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Was der dritte Parameter Deiner PowerMod-Funktion (module) für einen Zweck hat, ist unklar. Durch Experimente fand ich heraus, daß er, auf null gesetzt, zum gewünschten Ergebnis (Potenz) führt.

PowerMod wird z.B. zum Berechnen einer Potenz in Restklassen benötigt. Also u.a. bei RSA. konkret rechnet er a^b mod n, wobei er optimiert, indem er Result := Iff (b and 1 != 0, a*PowerMod(a,b-1,n), PowerMod(a,b/2,n)^2 ) mod n;

user profile iconDelphi-Laie hat folgendes geschrieben Zum zitierten Posting springen:
Kann es sein, daß Du intern mit dem Zahlensystem zur Basis 256 arbeitest? So kommt es mir immer wieder vor, wenn ich Deinen Quelltext an unterschiedlichsten Stellen zu analysieren mich bemühe.

Ergänzung: Der Geschwindigkeitszuwachs von BigNum_Lib zu BigNum2 ist phänomenal.

Jup, ich rechne mit Basis 256. Das vereinfacht einige Dinge in Bezug auf Überläufe dramatisch.


Delphi-Laie - Di 05.06.12 07:39

user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
Jup, ich rechne mit Basis 256. Das vereinfacht einige Dinge in Bezug auf Überläufe dramatisch.


Sicher wiederholt sich jetzt wenigstens teilweise, was schon an anderer Stelle in dieser Diskussion angeregt wurde: Die Erweiterung auf einen bzw. der Wechsel zu einem 32- oder gar 64-Bit-Integertyp bietet sich an, ja, drängt sich förmlich auf; inwieweit sie aber möglich ist, kann ich natürlich nicht einschätzen. Ich machte zwar schon reichlich an Algorithmen und Quelltexten rum, die ich nicht verstand, aber mich daran zu versuchen, dürfte doch eine oder mehr Nummer(n) zu groß für mich sein.

Leider kann ich nicht erkennen, welcher Algorithmus zur Multiplikation benutzt wurde, Karazuba als Idee fiel hier ja schon. Ich werfe einfach noch die russische Bauernmultiplikation ein. Ja, und dann noch FFT, das nach meinem Wissen Hagen Redmann für sein DEC benutzt. Aber das ist Dir sicher alles bekannt.

Eine meinerseits "handgeschriebene" schnelle Potenzierung ist sogar etwas schneller als Deine oben erwähnte, allerdings wird dabei nicht mit einem Rest (Modulo) hantiert, so daß sie für Ver-/Entschlüsselungen nicht geeignet sein dürfte.

Und beim BigNum_Lib wird bei der Funktion BigNumToStr das Argument verändert, was Ausgaben während der Rechnung(en) unmöglich macht, wenn man den Wert noch weiter benötigt. Ich versuchte es auch schon mit Übergaben an Dummywerte, mit denen ich dann die Funktion fütterte, doch auch hier wird das Original verändert, so, als sei das alles nur eine Datenstruktur und die Variablen letztlich nur Zeiger darauf. Seltsam...

Viel Spaß und Erfolg bei der weiteren Optimierung, und versuche bitte, Delphi-4-kompatibel zu bleiben!


Delphi-Laie - Mi 06.06.12 11:52

Hallo Benny!

In der Funktion BMD_FractionToStr wird eine - m.E. unnötige - fehlende Initialisierung der Variable "Digit" bemängelt. In der Funktion BM_Modulo hingegen ist eine Initialisierung (Einnullung) der Variable result am Funktionsanfang, spätestens am Anfang des ersten If-Blockes hingegen anscheinend nötig (kann bei großen Zahlen zu Fehlfunktionen führen), wird dort jedoch nicht als fehlend moniert. Alles weitere ausführlich in meiner PM mit Testprogramm.

Fazit: Hervorragende Unit, die durch unser aller Hilfe noch den letzten Feinschliff bekommt.


Delphi-Laie - Sa 09.06.12 12:31

In der Funktion BMD_BigNumToStr steht


Delphi-Quelltext
1:
If Result = '' Then Result := '0';                    


nur im if-Teil für die Hexausgabe, Dezimalzahlen werde so nicht mit erfaßt. Es sollte deshalb auch im else-Teil oder - noch besser - einfach am Ende der Funktion placiert werden.