Zitat:
bei deinen Programm zur berechnung von PI hast du ja aber auch verdammt viele nachkommastellen errechnet. Anscheind irgendwie durch die zeile: (var D: TIIntegerSplitData); register; ....
Die Pi Berechnung errechnet einen Integer ohne Nachkommastellen, man muß sich also das Komma gleich hinter der 1. Ziffer virtuell vorstellen. Wenn du also Pi mit DECMath zu 1 Million Nachkommastellen berechnest dann wird das Resulat in Wahrheit exakt 1 Million + 1 Dezimalstelle haben. Quasi Pi * 10^(1 Million).
Die Zeile mit (var D: TIIntegerSplitData); ist nur eine Callback Funktion von NBinarySplitting(). Mit dieser Methode werden Berechnungen von sehr großen transzendeten Zahlen sehr effizient durchgeführt. Ich kann dir dazu das PostScript von Bruno Haible mit seiner Dissertation dazu mailen. Dies diente mir als Vorlage meiner Implementation von NBinarySplitting() und ergo NPi(), NCos(), NSin(), NExp() etc. pp.
Zitat:
hätt'Sch noch ne kleine bzw. ne riesen große bitte: könntest du mal kurz die wichtigsten Funktionen w.z.b Wurzel, ... erläutern?
Vorweg, öffne die Datei NInts.pas im Ordner ..\
DEC????\LibInt\, dort ist die komplette Interface Sektion der IInteger drinnen. Dort sollte zu jeder Funktion eine mathematische Formel stehen was diese Funktion macht.
Es gibt im Schnittstellenkonzept vom DECMath mehrere grundsätzliche Regeln:
1.) alle Namen beginnen mit "N", -> NAdd(), NSub() etc.
2.) alle Namen sollten den mathematischen Kurznamen entsprechen -> NAdd() ist die Addition, NAddMod() die Addition modular zu einem Modul M.
3.) es gibt lesende und schreibbare Variablen. Alle ReadOnly Variablen sind am Ende der Parameterliste der Funktion und alle modifizierbaren Parameter -> die Resultate am Anfang der Funktion. Ergo NAdd(var A, const B, const C); bedeutet das in A das Resultat steht.
4.) Funktionen die ausschließlich nur CONST Parameter enthalten sind also reine ReadOnly Funktion und verändern NICHTS an den Parametern. Siehe NBit(const A, Index): Boolean; diese Funktion prüft ob das Bit mit Index in A gesetzt ist. Im Gegensatz zu NBit(var A; Index: Integer; NewBit: Boolean); das eine Modifizierende Funktion mit gleichem Namen ist. Hier wird das Bit mit Index in A auf NewBit gesetzt. Es gibt mehrerer solcher "doppeldeutigen" overloads im DECMath -> NBit(), NSgn(), NOdd() etc. pp.
5.) alle Parameter einer Funktion müssen NICHT distinct (unterschieden) sein. Beispiel NAdd(var A, const B,C); berechnet A := B + C; Man kann NAdd() aber auch so aufrufen NAdd(A,A,A) und überschreibt somit den vorherigen Inhalt von A mit A + A. Alle DECmath Funktionen können also mit beliebigen Parameterreihenfolge und sogar nicht unterschiedlichen Variablen aufgerufen werden und liefern denoch das korrekte Resultat. Bei den meisten Libraries müssen nämlich diese Paramater distinct sein, da ansonten falsche Resulate entstehen. Bsp. NAdd(B, A, B) und A = 1, B = 2 ergibt im DECMath B := 3 == 1 + 2; In den meisten anderen Libraries würde aber B = 2 schon intern auf 0 gesetzt werden und somit als Resulat B = 1 ergeben. Das liegt eben daran das die Programmierer dieser Libraries imer davon ausgehen das alle Paramater distinct, unterschiedlich zueinander sein müssen. Ein Aufruf wie NAdd(B,A,B) wäre demnach illegal in diesen Libs. Meistens wird eine interne Überprüfung uaf Distinct Params durchgeführt und eventuell eine
Exception ausgelösst. Ich erachte dies für ein völlig FALSCHES Schnittstellenkonzept, es verringert die Universalität der Lib und erhöht die nötig Aufmerksamkeitsschwelle des Programmierers um nicht solche Fehler zu produzieren. Im DECMath kannst du das alles Aufrufen wir du möchtest, es rechnet immer das was du ihm sagst.
6.) es gibt immer mindestens ZWEI Formen einer mathematischen Funktionen. Die jenigen die Inplaced auf dem Resulat arbeiten und diejenigen die quasi ein impliziertes Kopieren vornehmen. Bsp: NAdd(var A, const B) -> A := A + B ist eine inplaced Operation auf A mit B. Und NAdd(var A, const B,C) ist eine implizierte Kopierung, A := B + C, vom Resultat B + C nach A.
Dies hat ganz entscheidende Gründe. Denn ALLE internen mathematischen Operationen im DECMath arbeiten prinzipiell nach Möglichkeit mit einem implizietem Kopieren wenn dies möglich ist. Im Fall von NAdd(var A, const B,C) wird also intern mit 3 Speicherbreichen gearbeitet, A,B,C und sofort während man die Speicherbereich B + C rechnet auch nach A kopiert. Dabei ist es aus Sicht dieser LowLevel Funktionen meistens egal wenn zb. der Speicherbereich B gleich A ist, oder sogar A=B=C.
Auf alle Fälle wird durch diese interne Zwangslogik nach aussen für dich als Programmierer zwei Vorteile sichtbar. Erstens werden keine unnötigen Speicherkopierungen von einer Variable zu anderen notwendig, siehe JAVA und seine BigNums die quasi permamant Speicherinhalten verschieben müssen auf Grund ihres Klassendesigns. Das hat natürlich graviereden Einfluß auf die Performance. Der zweite Vorteil ist eben wiein Punkt 5.) beschrieben das die Parameterreihenfolgen aller Funktionen irrelevant ist, die Params müsen nicht distinct sein.
Und last but not least der dritte Vorteil ist das man denoch inplaced wie mit NAdd(A, A) arbeiten kann, und somit nur ein EINEN Speicherbereich arbeitet statt auf zweien mit vorheriger und/oder nachfolgender Kopieroperationen. Es ist also auch Speichereffizient und das ganz Konzept reduziert auf ein Minimum auf notwendigen Speicher udn Speicherkopierungen.
Wichtig ist aber immer eines ! Um all das musst du dich als Nutzer vom DECMath niemals kümmern, es ist also ein transparentes Schnittstellenkonzept das selbstständig in den meisten Fällen die effizienteste Taktik intern auswählt. Besonders deutlich wird dies in den Implementierungen der Funktionen NDiv(), NDivRem(), NDivMod().
7.) DECMath wählt unter Umständen innerhalb einer Funktion automatisch die effizientere andere Funktion aus. Bsp: NMul(A, A) ist identisch zu NSqr(A). Aber die Quadrierung einer Zahl ist im DECMath ca. 1.5 mal effizienter als NMul(A, A). Deshalb erkennt NMul() interne das eine Quadrierung möglich ist un ruft NSqr(A) auf. Im Falle von NMul(A, B, B) wird interne ergo NSqr(A, B) aufgerufen, usw.usw.
8.) auf Grund der Nutung von Interfaces hast du dich als Programmierer in keinster Weise um die Initialisierung und Finalisierung und Eindeutigkeit von IIntegern zu kümmern. Das erfolgt alles transparent. D.h. neben dem autom. Initialisieren deiner Variablen werden diese auch automatisch durch unsichtbare Try Finally Blöcke durch den Compiler geschützt wieder freigegeben. Das interne Refernece Counting im Zusammenhang mit der intenern Implementierung im DECMath sorgt auch für einen sogenannten "Copy on Write Demand" Effekt. Das ist defacto wie mit den LongStrings. Verweisen mehrere Variablen (LongStrigns/IInteger) auf einen gemeinsam benutzen Speicherbereich so wird vor der Modifikation einer dieser Variablen automatisch der gemeinsam benutze Inhalt des Speichers in eine Private Kopie für diese Variable kopiert. Im DECMath geht dies soweit das diese Kopierung nun selber innerhalb der LowLevel Funktionen ganz nebenbei in der eigentlichen mathematischen Berechnung erfolgt, siehe Punkt 6.)
Konstrukte wie
Delphi-Quellcode:
var
A,B,C: IInteger;
begin
NSet(A, 15);
// 1.)
B := A;
C := A;
// 2.)
NAdd(A, A, C);
end;
sind also möglich und führen bei 1.) dazu das alle drei Variablen A,B,C auf den gleichen Speicherbereich zeigen der 15 enthält. Erst bei 2.) wird A eine eigene neue "Kopie" erhalten müssen, in diesem Falle erkennt aber NAdd() intern das diese Kopieroperation intern impliziet druchgeführt werden kann wenn man auf LowLevel Ebene die Addition durchführt. Es wird also defakto rein garnichts an zusätzlichen Speicherkopierungen anfallen.
9.) über das Referenze Counting wird es möglich einen eingenen Garbage Collection Speichermenager zu installieren. Dieser befindet sich in
Unit NMath.pas. Im Gegensatz zu Borland MM ist dieser von Hause aus Threadsafe weil jeder Thread seine eigene Garbage Collection erhällt. Es entfällt also das meist unnötige aber sicherheitshalbere durchgeführte Locking per Critical Section wie im Borland MM notwendig. Dabei arbeitet das Garbage Collection mit dem installierten MM Hand in hand zusammen. D.h. aller Speicher wird vom normalen MM angefordert aber nicht zwangsläufig auch sofort wieder dahin freigegeben. Die Garbarge Collection wird also nur bei der Freigabe von IInteger aktiv und später dann bei der eventuelle Neuallozierung solch in der Garbage Collection gespeicherten IIntegern. Auf alle Fälle wird diese Garbage Collection im Durchschnitt ca. 10% mehr Performance bringen, egal ob man den Borland MM oder FastMM4 installiert hat.
10.) DECMath enthält inklusive in NMath.pas ein "Computation" Managament. Darunter versteht man die Möglichkeit eine eigene Idle-Callback zu installieren die dann periodisch in einem vorgebbaren Interval aufgerufen wird. Im Normalfalle wird man in einer solchen Callback dann Application.ProcessMessages() aufrufen und gegebenfalls durch eine Nutzeraktion mit Abort eine Berechnung abbrechen können. D.h. langandauerende Berechnungen im DECmath können sehr einfach so programmiert werden das die Anwendung nicht einfriert und die Berechnung sogar kontroliert von aussen abgebrochen werden kann. Wichtig dabei ist das im eigentlichen Source der mathematischen Funktionen nicht ein Fetzen an Source programmiert werden muß um dies alles zu erreichen. Auch hier wieder ist alles transparent und du als Programmierer kannst dich in deinen Mathematischen Funktion voll und ganz auf dein Ziel konzentrieren OHNE das du dort
GUI Funktionen implementieren musst.
Diese Idle-Callback ist nur gültig im MainThread einer Anwendung, in abgespalteten Threads macht sie ja auch keinen Sinn.
Soweit zum grundsätzlichen Schnittstellenkonzept, ich könnte noch tausend andere Sachen schreiben
Die mathematischen Funktionsnamen sollten im Grunde selbsterklärend sein. Also NAdd() Addition, NSub() Subtraktion, NMul() Multiplikation, NSqr() Quadrierung, NPow() die Exponentation, NSqrt() die Quadratwurzel, NRoot() die Wurzel zu einer beliebigen Basis -> A := A^(1/Basis), und falls vorhanden davon immer auch eine Modulare Version wie NAddMod(), NMulMod(), NPowMod() etc. pp.
Dann gibts noch die Setter und Getter Funktionen, wie alle NSet() Funktionen die irgendwas in einem jeweiligen Datenformat in einen IInteger umwandeln. Das können Integer, Int64, Floats, Strings und auch selber IIntger sein. Eine besondere Funktionsklasse dieser Setter Funktionen sind die NInt() Funktionen. Sie machen das gleiche wie ihre NSet() Pendanten aber eben als Funktionen die das Resultat als Funktionswert zurückgeben. Damit sind Sourcekonstrukte wie NAdd(A, NInt('12345')) möglich.
Die Getter Funktionen sind NStr() um einen IInteger in einen String zu konvertieren, NLong(): Cardinal; NFLoat(): Extended usw.
Dann gibts noch die logischen boolschen Operationen, NAnd(), NCut(), NShl(), NShr(), NXor(), NOr(), NBit(), NCpy(), NCpl() usw.
Fehlen noch einige Sonderfunktionen wie NSize() das die Größe eines IInteger in Bits, Bytes, Word oder Cardinals berechnen kann. NHigh() = NSize() -1;
Oder NDigitCount() das die Anzahl der Ziffern eines IIntgers zu einer vorgegebenen Basis berechnet. Je nach Paramater Exactly wird entweder ein angenähertes Resultat zurückgeliefter das entweder exakt ist oder aber um exakt +1 Ziffer größer ist als der tatsächliche IInteger. Oder eben bei Exactly = True wird mit hilfe einer etwas längerdauerenden Überprüfung dieser Fehler korregiert und NDigitCount() gibt immer die exakte Anzahl an Ziffern zur gewählten Basis zurück.
Jo soweit erstmal, wenn du noch weitere Fragen hast dann frag sie einfach
Zb. mit welchen Verfahren DECMath intern arbeitet, Bsp Quadratwurzel etc. pp.
Gruß Hagen