![]() | OptiVec
|
OptiCode
Dr. Martin Sander Software-Entwicklung Brahmsstr. 6 D-32756 Detmold http://www.optivec.de e-mail: optivec@gmx.de | Teil I.A: Handbuch |
Dieses HANDBUCH beschreibt die Grundlagen der OptiVec-Bibliotheken und gibt einen Überblick über VectorLib, den ersten Teil von OptiVec. Das objekt-orientierte Interface VecObj wird in Kap. 3 beschrieben. Die übrigen Teile von OptiVec werden in separaten Dateien dokumentiert, siehe MATRIXD.HTM und CMATHD.HTM.
Kap. 1.2 der vorliegenden Datei enthält die Lizenz-Bedingungen der Shareware-Version, Kap. 1.3 diejenigen der registrierten Vollversion.
OptiCode™ und OptiVec™ sind Warenzeichen von Dr. Martin Sander Software-Entwicklung. Andere zum Zwecke der Identifikation hier erwähnte Warenzeichen sind Eigentum der jeweiligen Hersteller.
1.1 Warum sich vektorisierte Programmierung auf dem PC lohnt |
1.1.1 Allgemeine Optimierungs-Strategien von OptiVec | |
1.1.2 Multi-Prozessor-Optimierung | |
1.1.3 Unterstützung für CUDA-Hardware | |
1.1.4 Auswahl der passenden OptiVec-Bibliothek |
1.2 Lizenzbedingungen der Shareware-Version | |
1.3 Registrierte Vollversion |
1.3.1 Preise und Bestellmöglichkeiten | |
1.3.2 Lizenzbedingungen |
1.4 Installation und Start |
4.7 Analysis | |
4.8 Signalverarbeitung: Fourier-Transformations-Techniken | |
4.9 Statistische Funktionen und Bausteine | |
4.10 Daten-Anpassung | |
4.11 Input und Output | |
4.12 Graphik |
Dem Ziel der Vereinfachung dienen zwar bereits seit mehr als zwei Jahrzehnten auch die Feldfunktionen von Fortran90 und templatisierte Vektor-Klassen in C++, doch sind dies lediglich Abkürzungen, die vom Compiler wieder in Schleifen übersetzt und entsprechend ineffizient verarbeitet werden. (Ähnliches gilt für die meisten der populären BLAS-Bibliotheken für Fortran). Demgegenüber bietet OptiVec eine hochoptimierte, in Assembler geschriebene Lösung, deren Geschwindigkeit nicht mehr durch die Qualität des Compilers, sondern nur noch durch die echte Geschwindigkeit des Prozessors bestimmt wird. Gegenüber compiliertem Code ergibt sich ein durchschnittlicher Geschwindigkeitsvorteil von einem Faktor 2 bis 3 (für einige Funktionen auch bis zu 8).
Nach unserem Kenntnisstand war OptiVec beim Erscheinen 1996 die erste umfassende Vektor- und Matrix-Bibliothek für PC-Compiler, die praktisch vollständig in Maschinensprache geschrieben wurde.
OptiVec tritt in Konkurrenz zu etlichen teuren integrierten Programmsystemen für wissenschaftliche und Datenverarbeitungs-Anwendungen, ist aber eben nicht ein "geschlossenes" integriertes Paket, sondern zur Verwendung mit den gängigen Programmiersprachen bestimmt, wodurch dem OptiVec-Benutzer die Flexibilität seiner bevorzugten Programmierumgebung erhalten bleibt.
Hier einige Stichworte:
Der große Funktions-Umfang, die hohe numerische Genauigkeit und die Einfachkeit der Benutzung machen OptiVec zu einem wertvollen Programmierwerkzeug für wissenschaftlich-technische Datenverarbeitungs-Anwendungen.
Die vorliegende Dokumentation beschreibt die Implementierungen von OptiVec für
Vektorisierung war schon immer die Zauberformel für Supercomputer mit ihren aus vielen einzelnen Prozessoren gebildeten Parallel-Architekturen. Auf diesen Architekturen wird versucht, die Rechenlast möglichst gleichmäßig auf alle Prozessoren zu verteilen und so die Ausführungsgeschwindigkeit zu maximieren. Die sogenanten "divide and conquer"-Algorithmen spalten kompliziertere numerische Aufgaben in kleine Schleifen über Vektorelemente auf. Hochgezüchtete Compiler finden dann den effizientesten Weg für die Verteilung der Vektor-Elemente auf die Prozessoren. Viele Compiler für Supercomputer enthalten bereits große Bibliotheken vordefinierter Vektor- und Matrixfunktionen für viele Anwendungszwecke. Diese Vektor- und Matrixfunktionen bieten den besten Weg, maximalen Datendurchsatz zu erzielen.
Es ist offensichtlich, daß die massive Parallelverarbeitung einer Cray auf den meisten PCs mit ihren eher bescheidenen 2, 4 oder allenfalls 8 Prozessor-Kernen nicht in gleicher Weise möglich ist. Auf den ersten Blick mag es daher sinnlos erscheinen, das Konzept der vektorisierten Programmierung auch auf dem PC anzuwenden. Tatsächlich aber sind auch viele vektor-spezifische Optimierungen möglich, selbst wenn nur eine CPU vorhanden ist. Viele dieser Optimierungen können von heutigen Compilern nicht direkt durchgeführt werden. Stattdessen muß der Programmierer auf Maschinensprachen-Niveau heruntergehen. Hand-optimierte, in Maschinensprache geschriebene Vektorfunktionen übertreffen compilierte Schleifen in der Geschwindigkeit durchschnittlich um einen Faktor von 2-3. Dies bedeutet, daß Vektorisierung die Mühe sehr wohl lohnen kann, auch für PC-Programme.
Hier sind die wichtigsten Optimierungs-Strategien, die in OptiVec zur Steigerung der Performance auf eingesetzt werden – unabhängig von der Zahl der Prozessor-Kerne:
Preload von Konstanten
Anstatt Konstanten für jeden einzelnen Funktionsaufruf innerhalb einer Schleife neu zu laden und wieder zu entladen, werden sie nur einmalig zu Beginn einer Vektor-Funktion geladen und stehen für die Verarbeitung sämtlicher Vektor-Elemente zur Verfügung.
Volle XMM- und FPU-AusnutzungWo immer nötig und sinnvoll, werden alle acht XMM-Register (in der 64-bit-Version sogar sechzehn) bzw. alle acht Coprozessor-Register eingesetzt (für einen Compiler ist es schon eine hervorragende Leistung, die Buchführung für vier Coprozessor-Register zu beherrschen).
Prefetch von Gruppen von Vektor-Elementen
Ab dem Pentium III stehen sehr nützliche "Prefetch"-Befehle zur Verfügung, die es erlauben, Daten schon genügend im voraus aus dem Hauptspeicher in den Prozessor zu laden, so daß sie gleich zur Verfügung stehen, wenn sie verarbeitet werden sollen.
Verwendung von SIMD-Befehlen
Man mag sich wundern, warum diese Strategie nicht gleich an erster Stelle genannt ist. Seit Einführung der SSE oder "Streaming Single-Instruction-Multiple-Data Extensions" mit dem Pentium III und zahlreichen Verbesserungen und Erweiterungen in jeder neuen Prozessor-Generation ist hier explizite Unterstützung für Vektor-Programmierung gegeben. Auf den ersten Blick sollten sie also die Vektor-Programmierung auf dem PC geradezu revolutionieren. Angesichts einer immer noch vorhandenen Diskrepanz zwischen Prozessor- und Datenbus-Geschwindigkeit sind aber viele der einfachen arithmetischen Operationen in ihrer Geschwindigkeit durch den Datenfluß begrenzt. Hier können SIMD-Befehle nur noch zu einem geringeren Geschwindigkeitsvorteil führen, als man eigentlich erwarten würde. Für kompliziertere Operationen wiederum können SIMD-Befehle oft gar nicht verwendet werden, wenn nämlich entweder bedingte Verzweigungen für jedes Vektor-Element individuell erforderlich sind, oder auch dann, wenn ohne die interne extended-Genauigkeit der FPU umständlichere Algorithmen gewählt werden müßten. OptiVec macht daher von den SSE-Befehlen überall dort Gebrauch, wo ein wirklicher Geschwindigkeitsvorteil erzielt werden kann. Man beachte allerdings, dass Operationen wie Matrix-Multiplikation oder Fourier-Transformation in float-Präzision zugunsten des hier möglichen erheblichen Geschwindigkeitsgewinnes einen Genauigkeitsverlust von 1-2 Stellen in Kauf nehmen. Wer demgegenüber auf maximale Genauigkeit Wert legen muss, sollte daher stets die ausschließlich FPU-Befehle verwendende P4-Version einsetzen.
Superscalar schedulingDurch sorgfältige Anordnung der Befehls-Folge können die parallelen Integer-Pipes und fadd/fmul-Einheiten moderner Prozessoren (seit Pentium) bestmöglich ausgenutzt werden.
Loop-unrolling
Auch dort, wo SIMD-Befehle nicht angewandt werden können oder wo eine optimale Ausnutzung der parallelen Prozessor-Pipes nicht für einzelne Vektor-Elemente erzielt werden kann, werden die Vektor-Elemente oft zu zweit, zu viert oder noch mehreren verarbeitet. Hierdurch werden einerseits die parallelen Pipes beschäftigt, andererseits auch der relative Anteil des Schleifen-Managements an der gesamten Ausführungszeit zurückgedrängt. Im Zusammenhang mit den oben beschriebenen "Prefetch"-Mechanismen wird die Schleifengröße möglichst an die Cache-Zeilengröße angepaßt.
Vereinfachte Adressierung
Die Adressierung von Vektor- und erst recht von Matrix-Elementen stellt noch immer eine Hauptquelle für ineffizienten Code heutiger Compiler dar. Durch Hin- und Herschaltung zwischen Eingabe- und Ausgabe-Vektoren wird eine große Zahl redundanter Adressierungs-Operationen ausgeführt. Durch die ebenso strikte wie einfache Definition "Verarbeitung von hier bis da" können die OptiVec-Funktionen den Aufwand für die Adressierung von Array-Elementen auf das nötige Minimum reduzieren.
Ersatz von Fließkomma- durch Ganzzahl-Befehle
Eine Reihe von Fließkomma-Operationen (wie Kopieren, Austauschen, Vergleich mit Sollwerten) kann wahlweise mit Ganzzahl- oder Fließkomma-Prozessorbefehlen implementiert werden. Hier wird natürlich die jeweils schnellste Methode angewandt.
Strikte Genauigkeits-Kontrolle
C/C++-Compiler wandeln eine float-Zahl oft in double um – 32-bit-Pascal/Delphi sogar in extended – bevor sie an eine mathematische Funktion übergeben wird. Diese Behandlung war einmal sinnvoll, als Festplattenspeicher zu teuer war, um in den .LIB-Dateien separate Funktionen für alle Datentypen einzuschließen. Auf heutigen PCs ist sie schlicht ineffizient. Konsequenterweise werden in den OptiVec-Routinen keine solchen impliziten Umwandlungen durchgeführt. Hier wird eine float-Funktion auch nur in float- (also einfacher) Genauigkeit berechnet, unter Verzicht auf die soundsovielte Stelle nach dem Komma, die ohnehin sofort wieder abgeschnitten wird. Zusätzlich kann V_setFPAccuracy( 1 ); aufgerufen werden, um die FPU auf einfache Genauigkeit umzuschalten, falls man sich generell mit dieser begnügen möchte. Hierdurch kann die Ausführungsgeschwindigkeit etwas gesteigert werden. Seien Sie aber darauf gefasst, dass die Genauigkeit Ihrer Endergebnisse noch deutlich unter der float-Spezifikation liegen kann, wenn schon die Zwischenergebnisse nur einfach-genau berechnet werden. Details werden bei V_setFPAccuracy aufgeführt.
Inline-Coding
Alle externen Funktionsaufrufe sind aus den Schleifen eliminiert. Dadurch wird die Ausführungszeit der "call / ret"-Paare sowie die Zeit für die Übergabe der Funktionsargumente eingespart.
Cache-line-Matching lokaler Variablen
Der Level−1-Cache aktueller Prozessoren ist in Zeilen von je 64 Byte organisiert (bei den Vorgängern waren es 32 Byte). Viele OptiVec-Funktionen benötigen doppelt- oder extended-genaue Variablen auf dem Stack (vor allem für Ganzzahl/Fließkomma-Umwandlungen oder für Bereichsprüfungen). 32-bit-Compiler und -Linker richten den Stack an 4-Byte-Grenzen aus. Es besteht also die Gefahr, dass die 8 Bytes einer double oder die 10 bytes einer extended beim Speichern auf dem Stack eine 64-Byte-Grenze überschreiten. Dies wiederum würde zu starken Geschwindigkeits-Einbußen durch Cache-Zeilenumbrüche führen. Um diese zu vermeiden, richten alle OptiVec-Funktionen, für die dies eine Rolle spielt, ihre lokalen Variablen an 8-Byte- (für double), 16-Byte- (für extended) bzw. 64-Byte-Grenzen aus (XMM- und YMM-Werte).
Ungeschützte und bereichsreduzierte Funktionen
OptiVec bietet alternative Formen einiger mathematischer Funktionen, bei denen man zwischen der geschützten Variante mit Fehlerbehandlung und einer ungeschützten Variante ohne Fehlerdetektion währen kann. In einigen Funktionen, die ganzzahlige Potenzen ausrechnen, erlaubt die Abwesenheit der Fehlerdetektion eine viel effizientere Codierung. Ähnliches gilt für die Sinus- und Cosinus-Funktion für mit Sicherheit zwischen -2p und +2p liegenden Argumenten. In diesen Spezialfällen kann die Ausführungszeit um bis zu 40% reduziert werden, abhängig von der Hardware-Umgebung. Dieser Geschwindigkeitsgewinn wird allerdings durch erhöhtes Risiko erkauft: Falls auch nur ein einziges Vektorelement außerhalb des gültigen Bereiches liegt, stürzen die ungeschützten und bereichsreduzierten Funktionen ohne Warnung einfach ab.
Multithread Support
Moderne Betriebssysteme erlauben es, innerhalb eines Programmes parallel laufende Threads auf die vorhandenen Prozessorkerne zu verteilen so die Performance gegenüber Single-Thread-Verarbeitung zu vervielfachen. Hierfür muß aber sichergestellt sein, daß in parallelen Threads laufende Funktionen sich nicht gegenseitig ihre Zwischenergebnisse überschreiben. Mit sehr wenigen Ausnahmen (namentlich den Plotting-Funktionen) sind alle übrigen OptiVec-Funktionen re-entrant, also darauf ausgerichtet, parallel zueinander laufen zu können.
Bei der Entwicklung Ihrer Multi-Thread-Anwendung stehen Ihnen zwei grundsätzlich verschiedene Optionen zur Verfügung: Funktionale Parallelität und Daten-Parallelität.
Funktionelle Parallelität
Verschiedene Threads führen verschiedene Aufgaben aus – sie unterscheiden sich in ihrer Funktion. Als Beispiel denke man an eine Anwendung, bei der ein Thread Benutzer-Ein- und Ausgaben abarbeitet, während ein anderer Thread Hintergrund-Berechnungen durchführt. Selbst auf einer Ein-Kern-CPU kann diese Art des Multi-Threading durch die vom Betriebssystem bewirkte ständige Umschaltung zwischen den beiden Threads Vorteile bieten (z.B., dass das Benutzer-Interface nicht blockiert, während die Hintergrundberechnungen ausgeführt werden, sondern weiterhin Eingaben annehmen kann). Auf einem Mehr-Prozessor-Computer können die zwei (oder mehr) Threads tatsächlich gleichzeitig auf den verschiedenen Prozessor-Kernen laufen. Normalerweise ist die Lastverteilung zwischen den Prozessoren bei funktionellem Multi-Threading alles andere als perfekt: Oft läuft ein Prozessor unter Volllast, während ein anderer arbeitslos auf Eingaben wartet. Dennoch ist diese Art des Multi-Threading die beste Option für Anwendungen, die nur kleine bis mittelgroße Vektoren und Matrizen umfassen.
Daten-Parallelität
Um die Lastverteilung zwischen den vorhandenen Prozessor-Kernen zu verbessern und so den Datendurchsatz zu maximieren, kann die klassische Parallelverarbeitung angewandt werden: Die Daten-Vektoren und -Matrizen werden in kleinere Teile zerlegt, und jeder Thread arbeitet einen solchen Teil ab. Die Brauchbarkeit dieses Ansatzes wird dadurch beschränkt, dass der Aufwand für die Verteilung der Daten auf die verschiedenen Threads und für die dabei nötige Kommunikation der Threads untereinander ziemlich hoch ist. Außerdem lassen sich die Daten niemals vollständig parallelisieren; es verbleibt immer ein gewisser Teil der Aufgaben, der nur sequentiell abgearbeitet werden kann. Daher lohnt sich Daten-Parallelität nur für größere Vektoren und Matrizen. Typische Schwellen-Größen, ab denen die Leistung mehrerer Prozessoren den für die Verteilung auf sie nötigen Aufwand "zurückverdient", reichen von unter 100 (bei mathematischen Funktionen komplex-zahliger Vektoren) bis zu über 10.000 Elementen (bei den einfachen arithmetischen Funktionen). Erst wenn die Vektoren / Matrizen deutlich größer als diese Schwellenwerte sind, kommt die erhöhte Leistung voll zum Tragen. Dann erst nähert sich die Beschleunigung dem theoretischen Grenzwert einer Verdopplung, Vervierfachung usw. an.
Für mittlere bis große Vektoren und Matrizen auf Mehrkern-Rechnern bietet sich die Verwendung der multi-core-optimierten Bibliotheken an. Diese verteilen für jede einzelne Funktion die Arbeitslast über die vorhandenen Prozessor-Kerne (Auto-Threading). Sie werden durch den Buchstaben "M" gekennzeichnet, also z.B. OVVC8M.LIB , ovgc64_9m.lib, etc. Diese Bibliotheken sind für Multiprozessor-Computer wie AMD64 X2, Intel i5, Core2Duo oder Workstations mit mehreren Prozessoren gedacht.
Die CUDA-Bibliotheks-Versionen basieren auf den "M"-Bibliotheken und lagern die Verarbeitung nur für sehr große Vektoren auf die Graphik-Karte aus. Sie sind durch den Buchstaben "C" markiert, z.B. OVBC64_8C.LIB.
Die "M"- und "C"- Bibliotheken laufen immer noch auf Ein-Kern-Computern. Durch die "Bürokratie-Verluste" beim Thread-Management sind sie hier aber deutlich langsamer als die Allzweck-Bibliotheken. Obwohl die "M"-Bibliotheken im Hinblick auf mittlere bis größere Vektoren entwickelt wurden, sind die Einbußen bei Verwendung mit kleinen Vektoren nicht sehr hoch, da die OptiVec Thread-Engine eine Funktion automatisch in einem einzelnen Thread ausführt, wenn die Vektor-Größe nicht ausreicht, um den Verteilungs-Aufwand durch die Parallel-Ausführung (oder gar durch die Auslagerung auf den Graphik-Prozessor) wieder aufzuholen.
Wenn Sie die "M"- oder "C"-Bibliotheken verwenden, muss Ihr Programm zu Beginn V_initMT( nVorhandeneProzKerne ) aufrufen.
Die folgenden Lizenzbedingungen gelten für die Shareware-Version von OptiVec. Die Lizenzbedingungen für die registrierte Vollversion finden Sie in Kap. 1.3.
Dies ist die Shareware-Version von OptiVec ("SOFTWARE").
Ihre Nutzung unterliegt den folgenden Lizenzbedingungen:
OptiVec für einzene Compiler: C++ Builder, Visual C++, GCC (Win), LLVM CLang (Win), Delphi, Lazarus / FreePascal oder Linux (GCC / CLang) | ||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||
OptiVec Master-Lizenz für alle unterstützten Compiler | ||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||
CMATH separat; für einzene Compiler: C++ Builder, Visual C++, GCC (Win), LLVM CLang (Win), Delphi, Lazarus / FreePascal oder Linux (GCC / CLang) | ||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||
CMATH separat; Master-Lizenz für alle unterstützten Compiler | ||||||||||||||||||||||||||||||
|
Die Preise incl. deutscher MWSt gelten innerhalb Deutschlands sowie für private Besteller innerhalb der EU. Falls Sie eine Europäische VAT ID besitzen oder von außerhalb der Europäischen Union bestellen, entfällt die deutsche MWSt. Dafür muß vom Erwerber der in seinem Heimatland gültige Satz abgeführt werden, bei Lieferung außerhalb der EU gegebenenfalls auch noch Import-Zoll.
Bestellungen bitte über www.optivec.de/order/ oder mit diesem Bestell-Formular an
OptiCode –Dr. Martin Sander Software Entwicklung
Brahmsstr. 6
D-32756 Detmold
optivec@gmx.de
Nach Zahlung der im Kaufpreis enthaltenen Lizenzgebühr dürfen Sie die SOFTWARE für unbegrenzte Zeit benutzen, sofern Sie das Copyright nicht verletzen und die folgenden Regeln einhalten:
OptiVec für C++ Builder (Embarcadero / Borland C++) | |
OptiVec für Visual C++ | |
OptiVec für GCC | |
OptiVec für LLVM CLang | |
OptiVec für Delphi | |
OptiVec für Lazarus / FreePascal |
Sie müssen nicht nur eine, sondern zwei OptiVec-Bibliotheken einschließen. Die erste, die "Basis-Bibliothek", enthält das Interface zwischen OptiVec und der BC-Laufzeitbibliothek. Bitte beachten Sie die notwendige Unterscheidung zwischen Verwendung der statischen und der dynamischen Laufzeitbibliothek in 32-bit:
Plattform | Compiler | statische Laufzeitbibliothek | Laufzeitbibliothek als DLL |
Win64 | bcc64, ab RAD Studio 12 (2023) | ovbcbase64.a | ovbcbase64.a |
bcc64, alle Versionen bis RAD Studio 11.x | ovbcx64.a | ovbcx64.a | |
Win32 | "klassischer" Borland Compiler bcc32
ab RAD Studio / BCB 12 (2023) | ovbcbase32s.lib | ovbcbase32d.lib |
ältere Versionen bis einschl. 11.x | vcfs.lib | vcfd.lib | |
CLang-Borland Compiler bcc32c
ab RAD Studio 12 | ovbcbase32cs.lib | ovbcbase32cd.lib | |
RAD Studio 10.x, 11.x | ovbc10_11base32cs.lib | ovbc10_11base32cd.lib |
Die zweite einzubindende Bibliothek wiederum ist entscheidet über die Prozessor-Unterstützung. Sie ist aber unabhängig von der jeweiligen Projekt-Konfiguration.
Plattform | Compiler | Prozessor | Allzweck | Debug | Autothreading (MP) | MP + CUDA |
Win64 | bcc64 | P9: Haswell+ / Excavator+ | OVVC64_9.a | ---- | OVBC64_9M.a | OVBC64_9C.a |
bcc64 | P8: AMD64xxx, Core2xxx | OVBC64_8.a | OVBC64_8D.a | OVBC64_8M.a | OVBC64_8C.a | |
Win32 | bcc32 (klassisch) | P8: AMD64xxx, Core2xxx | VCF8W.LIB | ---- | VCF8M.LIB | VCF8C.LIB |
P4: Max Kompatibilität, FPU-Genauigkeit | VCF4W.LIB | VCF4D.LIB | VCF4M.LIB | ---- | ||
bcc32c (CLang) ab C++ Builder 10.1 Berlin | P8: AMD64xxx, Core2xxx | ovbc32c_8.lib | ---- | ovbc32c_8m.lib | ovbc32c_8c.lib | |
P4: Max Kompatibilität, FPU-Genauigkeit | ovbc32c_4.lib | ovbc32c_4d.lib | ovbc32c_4m.lib | ---- | ||
bcc32c (CLang) bis C++ Builder 10 Seattle | P8: AMD64xxx, Core2xxx | VCF8W.LIB | ---- | VCF8M.LIB | VCF8C.LIB | |
P4: Max Kompatibilität, FPU-Genauigkeit | VCF4W.LIB | VCF4D.LIB | VCF4M.LIB | ---- |
Alle in früheren Versionen von OptiVec gültigen Einschränkungen für die Verwendung mit dem 32-bit CLang-Borland-Compiler bcc32c.exe sind aufgehoben.
Fortsetzung mit Kap. 1.5 Deklaration von OptiVec-Funktionen in C/C++
Plattform | Visual Studio Version | Runtime Debug DLL | Debug Static | Release DLL | Release Static |
Win64 | VS 2022 | OVVC17x64MDD.LIB | OVVCx64MTD.LIB | OVVC17x64MDR.LIB | OVVCx64MTR.LIB |
Win64 | VS 2019 | OVVC16x64MDD.LIB | OVVCx64MTD.LIB | OVVC16x64MDR.LIB | OVVCx64MTR.LIB |
VS 2017 | OVVC15x64MDD.LIB | OVVCx64MTD.LIB | OVVC15x64MDR.LIB | OVVCx64MTR.LIB | |
VS 2015 | OVVC14x64MDD.LIB | OVVCx64MTD.LIB | OVVC14x64MDR.LIB | OVVCx64MTR.LIB | |
VS 2013 | OVVC12x64MDD.LIB | OVVC8_12x64MTD.LIB | OVVC12x64MDR.LIB | OVVC8_12x64MTR.LIB | |
VS 2012 | OVVC11x64MDD.LIB | OVVC8_12x64MTD.LIB | OVVC11x64MDR.LIB | OVVC8_12x64MTR.LIB | |
VS 2010 | OVVC8x64MDR.LIB* | OVVC8_12x64MTD.LIB | OVVC8x64MDR.LIB | OVVC8_12x64MTR.LIB* | |
VS 2008 | OVVC8x64MDR.LIB* | OVVC8_12x64MTD.LIB | OVVC8x64MDR.LIB* | OVVC8_12x64MTR.LIB | |
VS 2005 | OVVC8x64MDD.LIB | OVVC8_12x64MTD.LIB | OVVC8x64MDR.LIB | OVVC8_12x64MTR.LIB | |
Win32 | VS 2022 | OVVC17MDD.LIB | OVVCMTD.LIB | OVVC17MDR.LIB | OVVCMTR.LIB |
VS 2019 | OVVC16MDD.LIB | OVVCMTD.LIB | OVVC16MDR.LIB | OVVCMTR.LIB | |
VS 2017 | OVVC15MDD.LIB | OVVCMTD.LIB | OVVC15MDR.LIB | OVVCMTR.LIB | |
VS 2015 | OVVC14MDD.LIB | OVVCMTD.LIB | OVVC14MDR.LIB | OVVCMTR.LIB | |
VS 2013 | OVVC12MDD.LIB | OVVC8_12MTD.LIB | OVVC12MDR.LIB | OVVC8_12MTR.LIB | |
VS 2012 | OVVC11MDD.LIB | OVVC8_12MTD.LIB | OVVC11MDR.LIB | OVVC8_12MTR.LIB | |
VS 2010 | OVVC10MDD.LIB | OVVC8_12MTD.LIB | OVVC10MDR.LIB | OVVC8_12MTR.LIB | |
VS 2008 | OVVC9MDD.LIB | OVVCMTD.LIB | OVVC9MDR.LIB | OVVCMTR.LIB | |
VS 2005 | OVVC8MDD.LIB | OVVC8_12MTD.LIB | OVVC8MDR.LIB | OVVC8_12MTR.LIB |
Man beachte, dass es eine gewisse Inkonsistenz in den Konfigurations-Bezeichnungen von Visual Studio gibt: Die Standard-Konfigurationen "Debug" und "Release" binden die Laufzeitbibliothek und MFC als DLL ein, stellen also die in früheren Versionen "Debug DLL" und "Release DLL" genannten Konfigurationen dar. Das bedeutet, dass die OptiVec-Basisbibliotheken OVVC??MDD.lib und OVVC??MDR.lib mit diesen Konfigurationen zu benutzen sind. Außerdem muss sichergestellt sein, dass die Laufzeit- und MFC-DLL's auf jedem Rechner installiert sind, auf dem eine so erstellte Anwendung laufen soll. Für viele Anwendungen empfiehlt es sich daher, Project / Properties / Configuration Properties / C/C++ / Code Generation / Runtime Library in "Multi-Thread Debug (/MTd)" oder "Multi-Thread Release (MT)" zu ändern, um durch statisches Linken die DLL-Redistributables zu vermeiden. Dies ist in der "DebugStatic"-Konfiguration in den Demo-Dateien von OptiVec so realisiert.
Als zweites fügen Sie nun die prozessor-spezifische OptiVec-Bibliothek gemäß folgender Tabelle hinzu:
Prozessor | Allzweck-Bibliothek | Debug-Bibliothek | MP / Mehr-Kern | Mehr-Kern + CUDA | 64-bit Allgemein | 64-bit Debug | 64-bit Mehrkern | 64-bit MP + CUDA |
P9: Haswell+ / Excavator+ | ---- | ---- | ---- | ---- | OVVC64_9.LIB | ---- | OVVC64_9M.LIB | OVVC64_9C.LIB |
P8: AMD64xxx, Core2xxx | OVVC8.LIB | ---- | OVVC8M.LIB | OVVC8C.LIB | OVVC64_8.LIB | OVVC64_8D.LIB | OVVC64_8M.LIB | OVVC64_8C.LIB |
P4: FPU-Genauigkeit, 486DX/Pentium | OVVC4.LIB | OVVC4D.LIB | OVVC4M.LIB | ---- | ---- | ---- | ---- | ---- |
Um die "C"-Bibliotheken (Mehrkern+CUDA) zu nutzen, müssen Sie außerdem die Import-Bibliothek OVVCCU32.lib (32-bit) oder OVVCCU64.lib (64-bit) einschließen und sicherstellen, dass sich die entsprechende DLL, OVVCCU32.DLL oder OVVCCU64.DLL entweder in dem Verzeichnis befindet, in dem Sie Ihr ausfürbares Programm erstellen, oder in einem anderen Verzeichnis, dass in der Windows-Umgebungsvariablen PATH aufgeführt ist. Sollten Sie unter 64-bit noch Unterstützung für alte CUDA-Hardware (Compute Capability 3.5, 5.0, 6.0) benötigen, schließen Sie OVVCCULEG64.lib anstelle von OVVCCU64.lib ein, die auf die DLL OVVCCULEG64.DLL verweist.
Fortsetzung mit Kap. 1.5 Deklaration von OptiVec-Funktionen in C/C++
Plattform | GCC-Thread-Modell | GCC-Exception-Modell | Passende OptiVec Basis-Bibliothek |
Win64 | Windows-Threads | SEH | ovgcbase64ws.lib |
Windows-Threads | Setjmp/Longjmp | ovgcbase64wj.lib | |
Posix-Threads | SEH | ovgcbase64ps.lib | |
Posix-Threads | Setjmp/Longjmp | ovgcbase64pj.lib | |
Win32 | Windows-Threads | Dwarf | ovgcbase32wd.lib |
Windows-Threads | Setjmp/Longjmp | ovgcbase32wj.lib | |
Posix-Threads | Dwarf | ovgcbase32pd.lib | |
Posix-Threads | Setjmp/Longjmp | ovgcbase32pj.lib |
Nun wählen Sie die zweite, Prozessor-spezifische OptiVec-Bibliothek aus der nächsten Tabelle:
Prozessor | 32-bit Allzweck | Debug | MP / Mehr-Kern | Mehr-Kern + CUDA | 64-bit Allgemein | 64-bit Debug | 64-bit Mehrkern | 64-bit MP + CUDA |
P9: Haswell+ / Excavator+ | ---- | ---- | ---- | ---- | ovgc64_9.lib | ---- | ovgc64_9m.lib | ovgc64_9c.lib |
P8: AMD64xxx, Core2xxx | ovgc32_8.lib | ---- | ovgc32_8m.lib | ovgc32_8c.lib | ovgc64_8.lib | ovgc64_8d.lib | ovgc64_8m.LIB | ovgc64_8c.lib |
P4: FPU-Genauigkeit, 486DX/Pentium | ovgc32_4.lib | ovgc32_4d.lib | ovgc32_4m.lib | ---- | ---- | ---- | ---- | ---- |
Um die "C"-Bibliotheken (Mehrkern+CUDA) zu nutzen, müssen Sie zusätzlich die DLL's ovgccu32.dll (32-bit) oder ovgccu64.dll (64-bit) an den Linker übergeben. Sollten Sie unter 64-bit noch Unterstützung für alte CUDA-Hardware (Compute Capability 3.5, 5.0, 6.0) benötigen, schließen Sie ovgcculeg64.dll anstelle von ovgccu64.dll ein. Anders als alle anderen Compiler benötigt GCC keine Import-Bibliothek, sondern kann direkt gegen die DLL linken (und sich die Import-Tabelle dabei selbst erstellen). Außerdem müssen Sie sicherstellen, dass die genannten DLLs sich entweder in dem Verzeichnis befinden, in dem Sie Ihr ausfürbares Programm erstellen, oder in einem anderen Verzeichnis, dass in der Windows-Umgebungsvariablen PATH aufgeführt ist.
Ein sehr wichtiger Punkt, den es bei der Arbeit mit GCC zu beachten gilt, ist, dass der Linker gegenseitige Abhängigkeiten zwischen eingeschlossenen Bibliotheken nicht selbständig auflöst. Da die Basis-Bibliothek und die Prozessor-spezifische Bibliothek von OptiVec aber gegenseitige Abhängigkeiten enthalten, bedeutet dies, dass die beiden paarweise mindestens zweimal eingeschlossen werden müssen. Wenn Sie Linker-Fehler über nicht gefundene OptiVec-Funktionsnamen erhalten, schließen Sie einfach dasselbe Bibliotheken-Paar noch ein weiteres Mal ein. Das Makefile für die Demo-Programme mag als Beispiel dienen.
GCC ist der einzige unter den "großen" Compilern, der in 64-bit den aus 80-bit Fließkommazahlen bestehenden Datentyp long double bzw. extended unterstützt. (Die anderen Compiler benutzen in 64-bit long double / extended einfach als Synonym für double). Dies ist ein sehr wertvolles Feature von GCC, da die zusätzliche Genauigkeit und Reichweite gelegentlich eine bedeutende Vereinfachung darstellen können. OptiVec unterstützt diesen Datentyp mit den VE_, VCE_, VPE_, ME_, und MCE_ Funktionen.
Die Linux-Version von OptiVec ist weiter unten beschrieben.
Fortsetzung mit Kap. 1.5 Deklaration von OptiVec-Funktionen in C/C++
Prozessor | 32-bit Allzweck | Debug | MP / Mehr-Kern | Mehr-Kern + CUDA | 64-bit Allgemein | 64-bit Debug | 64-bit Mehrkern | 64-bit MP + CUDA |
P9: Haswell+ / Excavator+ | ---- | ---- | ---- | ---- | ovcl64_9.lib | ---- | ovcl64_9m.lib | ovcl64_9c.lib |
P8: AMD64xxx, Core2xxx | ovcl32_8.lib | ---- | ovcl32_8m.lib | ovcl32_8c.lib | ovcl64_8.lib | ovcl64_8d.lib | ovcl64_8m.LIB | ovcl64_8c.lib |
P4: FPU-Genauigkeit, 486DX/Pentium | ovcl32_4.lib | ovcl32_4d.lib | ovcl32_4m.lib | ---- | ---- | ---- | ---- | ---- |
Um die "C"-Bibliotheken (Mehrkern+CUDA) zu nutzen, müssen Sie zusätzlich die Importbibliotheken ovvccu32.lib (32-bit) oder ovvccu64.lib (64-bit) an den Linker übergeben, die auf ovvccu32.dll bzw. ovvccu64.dll verweisen. Sollten Sie unter 64-bit noch Unterstützung für alte CUDA-Hardware (Compute Capability 3.5, 5.0, 6.0) benötigen, schließen Sie ovvcculeg64.lib anstelle von ovvccu64.lib ein, die auf die DLL ovvcculeg64.dll verweist. Außerdem müssen Sie sicherstellen, dass die genannten DLLs sich entweder in dem Verzeichnis befinden, in dem Sie Ihr ausfürbares Programm erstellen, oder in einem anderen Verzeichnis, dass in der Windows-Umgebungsvariablen PATH aufgeführt ist. Falls Sie sich über den auf Visual C++ hinweisenden Bibliotheksnamen ovvccu??.lib wundern in der Tat werden die cudaOptiVec-Bibliotheken für Visual C++ auch für CLang verwendet.
Die Linux-Version von OptiVec ist weiter unten beschrieben.
Fortsetzung mit Kap. 1.5 Deklaration von OptiVec-Funktionen in C/C++
Prozessor | 32-bit Allgemein | Debug | Autothreading (Multi-Proc.) | MP + CUDA | 64-bit Allgemein | 64-bit Debug | 64-bit Multi-Proc. | 64-bit MP+CUDA | 64-bit MP+alte CUDA |
P9: Haswell+ / Excavator+ | ---- | ---- | ---- | ---- | Win64\LIB9 | ---- | Win64\LIB9M | Win64\LIB9C | ---- |
P8: AMD64xxx, Core2xxx | LIB8 | ---- | LIB8M | LIB8C | Win64\LIB8 | Win64\LIB8D | Win64\LIB8M | Win64\LIB8C | Win64\LIB8Cleg |
P4: FPU-Genauigkeit, 486DX/Pentium | LIB4 | LIB4D | LIB4M | ---- | ---- | ---- | ---- | ---- | ---- |
Um die "C"-Units (Mehrkern+CUDA) zu nutzen, müssen Sie außerdem sicherstellen, dass sich OVDCU32.DLL (32-bit), OVDCU64.DLL (64-bit) oder OVDCULEG64.DLL (64-bit für alte CUDA-Devices mit Compute Cap. 3.5, 5.0, 6.0) entweder in dem Verzeichnis befindet, in dem Sie Ihr ausfürbares Programm erstellen, oder in einem anderen Verzeichnis, dass in der Windows-Umgebungsvariablen PATH aufgeführt ist.
Fortsetzung mit Kap. 1.5.2 Deklaration von OptiVec-Funktionen in Pascal / Delphi
Prozessor | Allgemein | Debug | Autothreading (Multi-Processor) | MP + CUDA | MP + alte CUDA |
P9: Haswell+ / Excavator+ | LIB9 | --- | LIB9M | LIB9C | --- |
P8: AMD64xxx, Core2xxx | LIB8 | LIB8D | LIB8M | LIB8C | LIB8Cleg |
Um die "C"-Units (Mehrkern+CUDA) zu nutzen, müssen Sie außerdem sicherstellen, dass sich OVDCU64.DLL (für aktuelle CUDA-Devices) oder OVDCULEG64.DLL (für alte CUDA-Devices mit Compute Capability 3.5, 5.0, 6.0) entweder in dem Verzeichnis befindet, in dem Sie Ihr ausfürbares Programm erstellen, oder in einem anderen Verzeichnis, dass in der Windows-Umgebungsvariablen PATH aufgeführt ist.
Fortsetzung mit Kap. 1.5.2 Deklaration von OptiVec-Funktionen in Pascal / Delphi
Plattform | Threading | OptiVec-Basis-Bibliothek |
Linux 64 | Single-thread | ovlxcbase64s.a |
Multi-thread | ovlxcbase64m.a |
Dann wähle man die zweite, prozessor-spezifische OptiVec-Bibliothek aus folgender Tabelle:
Processor | Allgemein | Debug | Multi-Proc. Auto-Threading |
P9: Haswell+ / Excavator+ | ovlxc64_9.a | ---- | ovlxc64_9m.a |
P8: AMD64xxx, Core2xxx | ovlxc64_8.a | ovlxc64_8d.a | ovlxc64_8m.a |
Ein sehr wichtiger Punkt, den es bei der Arbeit mit Linux-GCC und -CLang zu beachten gilt, ist, dass der Linker gegenseitige Abhängigkeiten zwischen eingeschlossenen Bibliotheken nicht selbständig auflöst. Da die Basis-Bibliothek und die Prozessor-spezifische Bibliothek von OptiVec aber gegenseitige Abhängigkeiten enthalten, bedeutet dies, dass die beiden paarweise mindestens zweimal eingeschlossen werden müssen. Wenn Sie Linker-Fehler über nicht gefundene OptiVec-Funktionsnamen erhalten, schließen Sie einfach dasselbe Bibliotheken-Paar noch ein weiteres Mal ein. Das Makefile für die Demo-Programme mag als Beispiel dienen.
64-bit-Integers (__int64 in BC++ Builder und MS Visual C++, Int64 in Delphi, früher einmal Comp in Turbo Pascal) werden in OptiVec als quad (für "quadruple integer", also Vierfach-Integer) bezeichnet.
Der Datentyp quad ist in 32-bit immer vorzeichenbehaftet; nur für Win64 bietet OptiVec den Datentyp uquad als vorzeichenlosen 64-bit Ganzzahltyp.
Der Pascal/Delphi-Benutzern wohlbekannte Datentyp extended wird in der Borland C++-Version von OptiVec als Synonym für long double verwendet. Da weder Visual C++ noch die meisten 64-bit-Compiler 80-bit-Fließkommazahlen unterstützen, ist extended hier als double definiert.
Der Grund für die Einführung des Typs extended ist, daß alle OptiVec-Funktionen identische Namen in C/C++ und Pascal/Delphi haben sollen. Die Funktions-Präfixe aber sind vom Datentyp der verarbeiteten Vektoren abgeleitet (s.u.). Der Buchstabe "L" (der vielleicht für long double stehen könnte) ist bereits durch long int und unsigned long überbelegt. So bietet sich der Buchstabe "E" für extended an, was den zusätzlichen Vorteil der Nähe zu den Buchstaben "D" für double und "F" für float hat. Es ist vorgesehen, in der Zukunft 128-bit-Fließkommazahlen (__fp128 / __float128) als "G" für Great und Half Floats (__fp16) als "H" ihren Platz ebenfalls in alphabetischer Nachbarschaft finden zu lassen.
Aus "historischen" Gründen weisen die Ganzzahl-Datentypen eine etwas konfuse Nomenklatur in Pascal/Delphi auf. Um die vom Datentyp abgeleiteten Präfixe mit der C/C++-Version von OptiVec kompatibel zu machen, definieren wir eine Anzahl von Synonymen, wie in der folgenden Tabelle beschrieben:
type | Pascal/Delphi-Name | Synonym | abgeleitetes Präfix |
8 bit signed | ShortInt | ByteInt | VBI_ |
8 bit unsigned | Byte | UByte | VUB_ |
16 bit signed | SmallInt | VSI_ | |
16 bit unsigned | Word | USmall | VUS_ |
32 bit signed | LongInt | VLI_ | |
32 bit unsigned | ULong | VUL_ | |
64 bit signed | Int64 | QuadInt | VQI_ |
64 bit unsigned | UInt64 | UQuad | VUQ_ |
16/32 bit signed | Integer | VI_ | |
16/32 bit unsigned | Cardinal | UInt | VU_ |
Um einen Bool'schen Datentyp derselben Größe wie Integer zur Verfügung zu haben, definieren wir den Typ IntBool. Er ist äquivalent mit LongBool. Man findet den Typ IntBool vor allem als Rückgabewert vieler mathematischer VectorLib-Funktionen.
Unsere Ziele sind
Die entsprechenden Definitionen für Pascal/Delphi sind in der Unit VecLib enthalten:
type fComplex = record Re, Im: Float; end;
type dComplex = record Re, Im: Double; end;
type eComplex = record Re, Im: Extended; end;
type fPolar = record Mag, Arg: Float; end;
type dPolar = record Mag, Arg: Double; end;
type ePolar = record Mag, Arg: Extended; end;
Komplexe Zahlen werden initialisiert, indem ihrem Real- und Imaginärteil bzw. ihrem Mag- und Arg-Teil die gewünschten Werte zugewiesen werden, z.B.:
z.Re = 3.0; z.Im = 5.7;
p.Mag = 8.8; p.Arg = 3.14;
(Für Pascal/Delphi muß der Zuweisungs-Operator natürlich ":=" geschrieben werden).
Alternativ kann die Initialisierung auch mittels der Funktionen fcplx oder fpolr durchgeführt werden:
C/C++:
z = fcplx( 3.0, 5.7 );
p = fpolr( 4.0, 0.7 );
Pascal/Delphi:
fcplx( z, 3.0, 5.7 );
fpolr( p, 3.0, 5.7 );
oder:
z := fcplx( 3.0, 5.7 );
p := fpolr( 3.0, 5.7 );
Für doppelt-genaue komplexe Zahlen gebrauche man dcplx und dpolr, für extended-genaue ecplx und epolr.
Zeiger auf komplexe Felder oder Vektoren werden mithilfe der Datentypen cfVector, cdVector und ceVector (für cartesisch-komplexe Vektoren) sowie pfVector, pdVector und peVector (für Vektoren komplexer Zahlen in Polarkoordinaten) definiert, wie unten beschrieben.
Die Basis aller VectorLib-Funktionen bilden die Vektor-Datentypen, die in <VecLib.h> bzw. der Unit VecLib definiert und unten aufgelistet sind. Im Unterschied zu den statischen Arrays, die immer eine beim Compilieren
festgelegte Größe besitzen, arbeiten die VectorLib-Typen mit dynamischer Speicherzuweisung und daher mit variablen Größen. Wegen dieser Flexibilität empfehlen wir den vorzugsweisen Gebrauch der letzteren. Hier sind sie also:
C/C++
| Pascal/Delphi
|
Intern handelt es sich also bei einem Datentyp wie fVector um einen "Zeiger auf float". Man sollte ihn sich allerdings lieber als "float-Vector" vorstellen.
N.B.: In der Windows-Programmierung wird häufig der Buchstabe "l" oder "L" eingesetzt, um long int-Variablen zu bezeichnen. Um Verwechslungen vorzubeugen, wird hier für long int stets das aus zwei Buchstaben bestehende Kürzel "li" oder "LI" verwendet und für unsigned long das Kürzel "ul" oder "UL". Konflikte mit den Präfixen für long double-Vektoren werden durch Ableitung deren Kürzel vom Alias-Namen extended und den Gebrauch von "e", "ce", "E" und "CE" umgangen, wie bereits oben und auch in den folgenden Abschnitten beschrieben. |
OptiVec-Vektoren | Pascal/Delphi-Arrays (statisch/dynamisch) | |
Ausrichtung des ersten Elements | an 64-byte-Grenze für optimale Cache-Zeilen-Anpassung | 2- oder 4-byte-Grenze (kann Zeilenumbruchs-Strafzyklen für double, QuadInt zur Folge haben) |
Ausrichtung folgender Elemente | gepackt (d.h. keine Dummy-Bytes zwischen Elementen, auch nicht für 10- und 20-bit-Typen) | Arrays müssen in Delphi als "packed" deklariert werden, um kompatibel mit OptiVec zu sein |
Index-Bereichsprüfung | keine | automatisch mittels eingebauter Größeninformation |
dynamische Speicherzuweisung | function VF_vector, VF_vector0 | procedure SetLength |
Initialisierung mit 0 | optional durch Aufruf von VF_vector0 | immer |
Freigabe | function V_free, V_freeAll | procedure Finalize |
einzelne Elemente lesen | function VF_element: a := VF_element(X,5); typecast in Array ebenfalls möglich: a := fArray(X)[5]; | Index in eckigen Klammern: a := X[5]; |
einzelne Elemente schreiben | function VF_setElement: VF_setElement(X,5,a); typecast in Array ebenfalls möglich: fArray(X)[5] := a; | Index in eckigen Klammern: X[5] := a; |
Übergabe an OptiVec-Funktion | direkt: VF_equ1( X, sz ); | Adress-Operator: VF_equ1( @X, sz ); |
Übergabe von Subvektor an OptiVec-Funktion | function VF_Pelement: VF_equC( VF_Pelement(X,10), sz−10, 3.7); | Adress-Operator: VF_equC( @X[10], sz−10, 3.7 ); |
Zum Präfix VF_ gehören Argumente vom Typ float und fVector, zu VD_ gehören double und dVector und natürlich entsprechend zu VE_ extended und eVector als Argumente und Rückgabewerte.
In den folgenden Kapiteln und in der Funktionsreferenz ist meist nur die VF_- Version einer Funktion explizit beschrieben. Die VD_- und VE_-Versionen sind dieser exakt analog. Man hat nur "fVector" durch "dVector" bzw. "eVector" zu ersetzen und "float" durch "double" bzw. "extended".
Rückgabewerte der komplexen Datentypen sind in Pascal/Delphi nicht möglich. Daher unterscheidet sich die Syntax komplexer Funktionen, die Werte zurückgeben, in C/C++ und Pascal/Delphi.
Im Unterschied zu der Sorglosigkeit, mit der komplex-mathematische Funktionen meist behandelt werden, sind die komplexen Funktionen von OptiVec so geschrieben, daß die volle Genauigkeit über den gesamten für den jeweiligen Datentyp legalen Bereich von Eingabe-/Ausgabewerten gewährleistet ist.
Wie bereits erwähnt, sind die nicht-vektorisierten komplexen Funktionen in der Datei CMATHD.HTM beschrieben.
Für die vorzeichenlosen Ganzzahl-Typen gilt analog:
Präfix VUB_: Datentyp unsigned char (unsigned byte) / UByte,
Präfix VUS_: Datentyp unsigned short / USmall,
Präfix VU_: Datentyp unsigned / UInt,
Präfix VUL_: Datentyp unsigned long / ULong, (32-bit für Windows, 64-bit für Linux)
Präfix VUQ_: Datentyp uquad / UQuad (nur für Win64 und Linux),
Präfix VUI_: Datentyp ui.
Man erschrecke nicht vor dieser Vielfalt. Es gehört zu den Stärken moderner Computer-Sprachen, über diese vielen Datentypen zu verfügen. Gleichzeitig kann diese Stärke natürlich eine Schwäche bedeuten, denn sie verführt zu einem Programmierstil, in dem die Datentypen so lange vermischt werden, bis der Durchblick verloren geht. Im Normalfall sollten die VI_-, VLI_-, VU_- und VUI_- Versionen vollauf genügen. Es ist aber nicht schlecht zu wissen, daß noch mehr zur Verfügung stehen, wenn man sie denn benötigt.
Sofern sie existieren, sind die mit Ganzzahlen arbeitenden VectorLib-Funktionen gemeinsam mit ihren Fließkomma-Schwestern besprochen. Auch auf die Gefahr hin, Sie zu langweilen: Die VI_-Version gewinnt man aus der VF_-Version durch Ersatz aller "float"-Parameter durch "int" und aller "fVector" durch "iVector". Gleiches gilt für alle anderen Ganzzahl-Typen.
MS Visual C++ und Borland C++ Builder (nicht aber frühere Borland C++-Versionen): Die Direktive
"using namespace OptiVec;"
sollte entweder im Funktionskörper jeder ein tVecObj verwendenden Funktion oder im globalen Deklarationsteil eines Programmes auftauchen. Der Platz in den einzelnen Funktionskörpern ist sicherer, da er potentielle Namespace-Konflikte mit anderen Funktionen vermeidet.
Die Vektor-Objekte werden als classes vector<T> implementiert, die die Vektor-Adresse (den Zeiger) und seine Größe size kapseln.
Für einfachere Verwendung wurden diesen Klassen Alias-Namen zugewiesen als fVecObj, dVecObj usw., wobei der Datentyp wie sonst in OptiVec durch den ersten oder die ersten beiden Buchstaben des Klassennamens angezeigt wird.
Alle VectorLib für einen bestimmten Datentyp definierten Funktionen sind als Member-Funktionen der betreffenden class tVecObj enthalten.
Die Konstruktoren können vier Formen annehmen:
vector(); // kein Speicher zugewiesen; size auf 0 gesetzt
vector( ui size ); // Vektor von size Elementen erzeugt
vector( ui size, T fill ); // desgleichen, aber mit "fill" initialisiert
vector( vector<T> init ); // erzeugt eine Kopie des Vektors "init"
Für alle Vektor-Klassen sind die arithmetischen Operatoren
+ - * / += -= *= /=
definiert, mit der Ausnahme, daß für die polar-komplexen Vektor-Klassen nur Multiplikationen und Divisionen, nicht aber Addition und Subtraktion unterstützt werden. Diese Operatoren stellen den einzigen Fall dar, in dem das Ergebnis einer Berechnung direkt einem Vektor-Objekt zugewiesen werden kann, wie z.B.
fVecObj Z = X + Y; oder
fVecObj Z = X * 3.5;
Man beachte aber, daß die Syntax-Regeln von C++ eine wirklich effiziente Implementierung dieser Operatoren nicht zulassen. Die arithmetischen Member-Funktionen sind wesentlich schneller. Wenn es auf Rechengeschwindigkeit ankommt, benutze man daher die letzteren anstelle der Operatoren-Syntax:
fVecObj Z.addV( X, Y ); oder
fVecObj Z.mulC( X, 3.5 );
Der Operator * bedeutet Multiplikation der einzelnen Elemente miteinander und nicht das Skalarprodukt zwier Vektoren.
Alle übrigen arithmetischen und mathematischen Funktionen können nur als Member-Funktion des betreffenden Ausgabe-Vektors aufgerufen werden, wie z.B. Y.exp(X). Obwohl es sicher logischer wäre, auch diese Funktionen so zu definieren, daß man stattdessen "Y = exp(X)" schreiben könnte, wurde die Syntax der Member-Funktionen gewählt, da sie wesentlich effizienter implementiert werden kann: Der einzige Weg, die zweite Variante zu implementieren, besteht darin, das Ergebnis der jeweiligen Funktion in einem temporären Vektor zwischenzuspeichern, der anschließend in Y kopiert wird. Hierdurch werden Rechenaufwand und Speicheranforderungen erhöht. Wir sind aber an Ihrer Meinung interessiert: Würden Sie trotzdem die Syntax "Y = func(X);" gegenüber der Member-Funktions-Syntax "Y.func(X);" vorziehen und ihre Nachteile in Kauf nehmen wollen? Bitte senden Sie uns Ihren Kommentar an support@optivec.com. Diese Syntax könnte in späteren Versionen von VecObj zur Verfügung gestellt werden.
Während die meisten VecObj-Funktionen Member-Funktionen des Ausgabe-Vektors sind, gibt es einige Funktionen, die gar keinen Ausgabe-Vektor haben. In diesen Fällen sind die Funktionen Member-Funktionen eines Eingabe-Vektors.
Beispiel: s = X.mean();.
Sollten Sie einmal in die Lage kommen, ein VecObj-Vektorobjekt mit einer "klassischen" C-VectorLib-Funktion verarbeiten zu wollen (z.B., um nur einen Teil zu verarbeiten), rufen Sie bitte die Member-Funktionen
getSize() für die Vektorlänge,
getVector() für den Zeiger (vom Typ tVector) oder
Pelement( n ), um einen Zeiger auf das n'te Element zu bekommen.
Die Syntax aller VecObj-Funktionen wird in FUNKREF.HTM zusammen mit den zugrundeliegenden VectorLib-Funktionen behandelt, die von tVecObj gekapselt werden.
VF_vector | Speicherzuweisung für einen Vektor |
VF_vector0 | Speicherzuweisung in Initialisierung aller elemente mit 0 |
V_free | einen Vektor freigeben |
V_nfree | mehrere Vektoren freigeben (nur C/C++) |
V_freeAll | elle existierenden Vektoren freigeben |
Daß den bereits in C und C++ reichlich vorhandenen Operatoren und Funktionen für diese Zwecke (malloc, calloc, free, new, LocalAlloc, GlobalAlloc usw.) hier noch mehr hinzugefügt werden, hat seinen Grund darin, daß für jedes Speichermodell und jede Umgebung automatisch die optimale Methode gewählt werden soll. Die Implementation von VF_vector usw. paßt sich also der jeweiligen Umgebung an, was der Portabilität der Programme zugute kommt.
C/C++: X = VF_vector( 3*size); Z = (Y = X+size) + size; |
Pascal/Delphi: X := VF_vector( 3*size ); Y := VF_Pelement( X, size ); Z := VF_Pelement( Y, size ); |
Zur Initialisierung ganzer Vektoren (nach der Zuweisung von Speicherplatz!) mit bestimmten Werten stehen die folgenden Funktionen zur Verfügung:
VF_equ0 | setzt alle Elemente gleich 0 |
VCF_Reequ0 | setzt alle Realteile gleich 0, wobei die Imaginärteile unverändert bleiben |
VCF_Imequ0 | setzt alle Imaginärteile gleich 0, wobei die Realteile unverändert bleiben |
VF_equ1 | setzt alle Elemente gleich 1 |
VF_equm1 | setzt alle Elemente gleich −1 |
VF_equC | setzt alle Elemente gleich C |
VF_equV | macht einen Vektor zur Kopie eines anderen |
VFx_equV | erweiterte Version des Gleichheitsoperators: Yi = a * Xi + b |
VF_ramp | "Rampe": Xi = a * i + b. |
VUI_ramp | "Index-Rampe": Xi = i (nur VUI_ und VU_-Versionen) |
VF_randomLC | Zufallszahlen hoher Qualität |
VF_random | vereinfachte Form von VF_randomLC für Zufallszahlen hoher Qualität |
VF_noiseLC | weißes Rauschen |
VF_noise | vereinfachte Form von VF_noiseLC für weißes Rauschen |
VF_comb | "Kamm", der an äquidistanten Punkten gleich einer Konstanten C und ansonsten 0 ist. |
Die folgenden Funktionen dienen dem Zugriff auf einzelne Vektor-Elemente:
VF_Pelement | gibt einen Zeiger auf das durch seinen Index bestimmte Vektor-Element zurück |
VF_element | gibt ein bestimmtes Vektor-Element zurück |
VF_getElement | kopiert ein bestimmtes Vektor-Element in eine Variable |
VF_setElement | setzt ein Vektor-Element auf einen neuen Wert |
VF_accElement | X[i] += c; addiert einen Wert zu einem Vektor-Element |
VF_decElement | X[i] -= c; subtrahiert einen Wert von einem Vektor-Element |
VF_Hann | Hann-Fenster |
VF_Parzen | Parzen-Fenster |
VF_Welch | Welch-Fenster |
VF_ReImtoC | zwei reelle Vektoren, Re und Im, zu einem cartesisch-komplexen Vektor zusammenfassen |
VF_RetoC | Realteil eines cartesisch-komplexen Vektors überschreiben |
VF_ImtoC | Imaginärteil eines cartesisch-komplexen Vektors überschreiben |
VF_PolartoC | Cartesisch-komplexen Vektor aus Polarkoordinaten konstruieren, die als getrennte Vektoren Mag und Arg vorliegen |
VF_MagArgtoP | zwei reelle Vektoren, Mag und Arg, zu einem polar-komplexen Vektor zusammenfassen |
VF_MagArgtoPrincipal | zwei reelle Vektoren, Mag und Arg, zu einem polar-komplexen Vektor zusammenfassen und die Zeigerwinkel auf den Hauptwert normieren: -p < Arg ≤ +p |
VF_MagtoP | Mag-Teil eines polar-komplexen Vektors überschreiben |
VF_ArgtoP | Arg-Teil eines polar-komplexen Vektors überschreiben |
VF_ReImtoP | Polar-komplexen Vektor aus cartesischen Koordinaten konstruieren, die als getrennte Vektoren Re und Im vorliegen |
VF_rev | kehrt die Reihenfolge aller Elemente eines Vektor um (engl. to reverse = umkehren) |
VCF_revconj | komplex-konjugierte Form in umgekehrter Element-Reihenfolge |
VF_reflect | reflektiert einen Vektor an seinem Mittelpunkt, so daß die obere Hälfte gleich der umgekehrten unteren Hälfte wird |
VF_rotate | rotiert einen Vektor um eine als Parameter übergebene Anzahl von Positionen (wobei auf einer Seite hinausgeschobene Elemente an der anderen wieder hineingeschoben werden) |
VF_rotate_buf | Effiziente Rotation eines Vektors unter Verwendung von benutzerspezifiziertem Pufferspeicher |
VF_insert | Element einfügen |
VF_delete | Element entfernen |
VF_sort | Sortieren (aufsteigende oder abfallende Folge) |
VF_sortind | mit einem Vektor assoziierten Index-Array sortieren |
VF_subvector | Unter-Vektor aus einem (meist größeren) Vektor extrahieren mit konstantem Abtastintervall |
VF_indpick | füllt einen Vektor mit Elementen eines anderen, wobei diese entsprechend ihren Indizes "herausgepickt" werden. |
VF_indput | verteilt die Elemente eines Vektors auf die durch ihre Indizes spezifizierten Plätze eines anderen Vektors. |
Operationen, die nur auf eine Untermenge von Elementen (z.B. auf jedes vierte, zehnte o.ä.) angewandt werden sollen, werden durch die Funktionsfamilie VF_subvector_... zur Verfügung gestellt, wobei die drei Punkte für ein Suffix stehen, daß die jeweilige Operation bezeichnet:
|
|
Dem Durchsuchen von Tabellen nach bestimmten Werten dienen:
VF_searchC | sucht das einem Wert C am nächsten kommende Element eines Vektors, wobei ein Parameter mode darüber entscheidet, ob das absolut nächste, das nächste "größere-oder-gleiche" oder das nächste "kleinere-oder-gleiche" gewählt werden |
VF_searchV | dasselbe für ein ganzes Feld von Suchwerten |
VF_polyinterpol | Polynom-Interpolation |
VF_ratinterpol | rationale Interpolation |
VF_natCubSplineInterpol | "natürliche" kubische Spline-Interpolation |
VF_splineinterpol | allgemeine kubische Spline-Interpolation |
V_FtoD | float in double |
V_CDtoCF | complex<double> in complex<float> (mit Überlauf-Abfang) |
V_PFtoPE | polar<float> in polar<extended> |
VF_PtoC | polar<float> in complex<float> |
V_ItoLI | int in long int |
V_ULtoUS | unsigned long in unsigned short |
V_ItoU | signed int in unsigned int. Die Umwandlungen zwischen vorzeichenbehafteten und vorzeichenlosen Typen beschränken sich allerdings auf Umwandlungen innerhalb der jeweils selben Genauigkeitsstufe. Funktionen wie "V_UStoLI" existieren naher nicht. |
V_ItoF | int to float |
VF_roundtoI | round to the closest integer |
VF_choptoI | round by neglecting ("chopping off") the fractional part |
VF_trunctoI | the same as VF_choptoI |
VF_ceiltoI | round to the next greater-or-equal integer |
VF_floortoI | round to the next smaller-or-equal integer |
So wird man im Datentyp short / SmallInt als Ergebnis der Multiplikation 5 * 20000 die vielleicht unerwartete Zahl -31072 erhalten. Das "korrekte" Resultat, 100000, übersteigt den darstellbaren Bereich von short/SmallInt-Zahlen, -32768 ≤ x ≤ +32767. short / SmallInt sind 16-bit-Zahlen, also ist n = 16 und 2n = 65536. Man kann sich das Zustandekommen des erhaltenen Resultates also so vorstellen, als ob der Prozessor 2 * 65536 = 131072 von 100000 abgezogen hätte, was -31072 ergibt.
Überlaufende Zwischenergebnisse werden nicht durch nachfolgende Operationen "geheilt". Das Ergebnis der Berechnung von (5 * 20000) / 4 ist nicht – wie man vielleicht hoffen könnte – +25000, sondern -7768.
VF_ReImtoC | Bildung eines cartesisch-komplexen Vektors aus Real- und Imaginärteil |
VF_RetoC | Überschreiben des Realteils |
VF_ImtoC | Überschreiben des Imaginärteils |
VF_CtoReIm | Extraktion von Real- und Imaginärteil |
VF_CtoRe | Extraktion des Realteils |
VF_CtoIm | Extraktion des Imaginärteils |
VF_PolartoC | Bildung eines cartesisch-komplexen Vektors aus Polarkoordinaten, die als separate reelle Vektoren Mag und Arg vorliegen |
VF_CtoPolar | Umwandlung eines cartesisch-komplexen Vektors in Polarkoordinaten, die in separaten reellen Vektoren Mag und Arg abgelegt werden |
VF_CtoAbs | Absolutwert (Zeigergröße in der komplexen Ebene) |
VF_CtoArg | Argument (Zeigerwinkel in der komplexen Ebene) |
VF_CtoNorm | Norm (hier als Quadrat des Absolutwertes definiert) |
VCF_normtoC | Norm, gespeichert als komplexer Vektor (mit allen Imaginärteilen gleich 0) |
VF_MagArgtoP | zwei reelle Vektoren, Mag und Arg, zu einem polar-komplexen Vektor zusammenfassen |
VF_MagArgtoPrincipal | dasselbe, mit Normierung der Zeigerwinkel auf den Hauptwert, -p < Arg ≤ +p |
VF_MagtoP | Mag-Teil überschreiben |
VF_ArgtoP | Arg-Teil überschreiben |
VF_PtoMagArg | Mag- und Arg-Teile extrahieren |
VF_PtoMag | Mag-Teil extrahieren |
VF_PtoArg | Arg-Teil extrahieren |
VF_PtoNorm | Norm (hier als Quadrat der Zeigerlänge definiert) |
VF_ReImtoP | polar-komplexen Vektor aus cartesischen Koordinaten konstruieren, die als separate reelle Vektoren Re und Im vorliegen |
VF_PtoReIm | polar-komplexen-Vektor in cartesische Koordinaten umwandeln, die als zwei reelle Vektoren Re und Im gespeichert werden |
VF_PtoRe | Realteil berechnen |
VF_PtoIm | Imaginärteil berechnen |
VPF_principal | Hauptwert. Man erinnere sich, daß eine komplexe Zahl unendlich viele Darstellungen in Polarkoordinaten besitzt, deren Zeigerwinkel sich um ganzzahlige Vielfache von 2 p unterschieden. Die Darstellung mit -p < Arg ≤ +p wird Hauptwert genannt. |
Mit Ausnahme der einfachen arithmetischen Funktionen (Addition, Subtraktion etc.), existieren die mathematischen Funktionen nur für die Fließkomma-Datentypen. Die meisten mathematischen Funktionen von VectorLib sind als vektorisierte Versionen der entsprechenden Funktionen von ANSI-C oder Pascal zu verstehen oder von diesen abgeleitet. In C/C++ werden Fehler grundsätzlich über die vom Compiler zur Verfügung gestellten (oder vom Anwender selbst geschriebenen) Funktionen _matherr und (bei Borland C++) _matherrl behandelt. In Pascal/Delphi bietet OptiVec dem Anwender die Möglichkeit, das Verhalten im Fehlerfall über die Funktion V_setFPErrorHandling festzulegen.
Zusätzlich zu dieser Fehlerbehandlung "pro Element" wird noch durch den Rückgabewert der Vektor-Funktion angegeben, ob die Berechnung für alle Elemente erfolgreich war oder ob irgendein Fehler auftrat. Im Falle fehlerfreier Verarbeitung ist der Rückgabewert FALSE (0), sonst TRUE (irgendeine Zahl ungleich 0).
VF_round | auf die nächstliegende ganze Zahl auf- oder abrunden |
VF_chop | Nachkommastellen abschneiden (Rundung Richtung 0) |
VF_trunc | identisch mit VF_chop |
VF_ceil | aufrunden |
VF_floor | abrunden |
VF_roundtoI | auf die nächstliegende ganze Zahl auf- oder abrunden |
VF_choptoI | Nachkommastellen abschneiden (Rundung Richtung 0) |
VF_trunctoI | identisch mit VF_choptoI |
VF_ceiltoI | aufrunden |
VF_floortoI | abrunden |
VF_choptoSI | Nachkommastellen abschneiden und als short / SmallInt speichern |
VF_ceiltoLI | aufrunden und als long / LongInt speichern |
VF_floortoQI | abrunden und als quad / QuadInt speichern |
VF_roundtoU | auf die nächstliegende ganze Zahl runden und als unsigned / UInt speichern |
VF_ceiltoUS | aufrunden und als unsigned short / USMall speichern |
VD_choptoUL | Nachkommastellen abschneiden und als unsigned long / ULong speichern |
VF_cmp0 | Vergleich mit 0 (signum-Funktion) |
VD_cmpC | Vergleich mit einem Sollwert C |
VE_cmpV | Vergleich korrespondierender Vektorelemente |
Die zweite Möglichkeit ist, dass –durch einen Unterstrich abgesetzt –die zu testende Bedingung angegeben wird. Das Resultat ist TRUE oder FALSE und wird als 1 oder 0, wieder in demselben Datentyp wie die Eingabe-Vektoren, gespeichert. Der Ergebnis-Vektor kann dann beispielsweise in Zeitreihen-Analyse-Aufgaben mit dem Eingabe-Vektor multipliziert werden, um nicht-konforme Eingabe-Elemente zu eliminieren. Beispiele sind:
VF_cmp_eq0 / _eqC / _eqV | Xi = 0 / C / Yi ? (engl. "equal") |
VF_cmp_ne0 / _neC / _neV | Xi ≠ 0 / C / Yi ? (engl. "not equal") |
VF_cmp_gt0 / _gtC / _gtV | Xi > 0 / C / Yi ? (engl. "greater than") |
VF_cmp_ge0 / _geC / _geV | Xi ≥ 0 / C / Yi ? (engl. "greater or equal") |
VF_cmp_lt0 / _ltC / _ltV | Xi < 0 / C / Yi ? (engl. "less than") |
VF_cmp_le0 / _leC / _leV | Xi ≤ 0 / C / Yi ? (engl. "less or equal") |
VF_cmp_stV | Xi ≈ Yi ? (engl. "similar to") |
VF_cmp_dtV | Xi ≉ Yi ? (engl. "dissimilar to") |
Als dritte Möglichkeit schießlich können die Indizes derjenigen Elemente in einem Index-Array abgelegt werden, für die die Bedingung als TRUE gefunden wurde, z.B.:
VF_cmp_neCind | Indizes der Elemente Xi != C |
VD_cmp_lt0ind | Indizes der Elemente Xi < 0 |
VE_cmp_geVind | Indizes der Elemente Xi ≥ Yi |
Während die eben beschriebenen einfachen Vergleichsfunktionen nur bezüglich einer einzigen Grenze testen, existieren einige Funktionen, die feststellen, ob Vektorelemente in einen durch zwei Grenzpunkte bestimmten Bereich fallen:
VF_cmp_inclrange0C | TRUE für 0 ≤ x ≤ C (C positiv), 0 ≥ x ≥ C (C negativ) |
VF_cmp_exclrange0C | TRUE für 0 < x < C (C positiv), 0 > x > C (C negativ) |
VF_cmp_inclrangeCC | TRUE für CLo ≤ x ≤ CHi |
VF_cmp_exclrangeCC | TRUE für CLo < x < CHi |
VF_cmp_inclrange0Cind | Indizes der Elemente 0 ≤ Xi ≤ C (C positiv) oder 0 ≥ Xi > C (C negativ) |
VF_cmp_exclrange0Cind | Indizes der Elemente 0 < Xi < C (C positiv) oder 0 > Xi > C (C negativ) |
VF_cmp_inclrangeCCind | Indizes der Elemente CLo ≤ Xi ≤ CHi |
VF_cmp_exclrangeCCind | Indizes der Elemente CLo < Xi < CHi |
Interessiert nicht das Ergebnis für jedes einzelne Vektor-Element, sondern nur die Anzahl der Elemente, so erhält man diese durch die Funktionen der VF_cnt_...-Familie ("cnt" kurz für engl. "to count" = "zählen"), z.B.:
VF_cnt_eq0 | Anzahl der Elemente Xi = 0 |
VF_cnt_gtC | Anzahl der Elemente Xi > C |
VF_cnt_inclrangeCC | Anzahl der Elemente CLo ≤ Xi ≤ CHi |
VF_cnt_leV | Anzahl der Elemente Xi ≤ Yi |
VF_cmp_ne0 / _neC / _neV | Xi != 0 / C / Yi ? (engl. "not equal") |
Um zu testen, ob sich in einer Tabelle bestimmte Werte finden, können die folgenden Funktionen benutzt werden:
VF_iselementC | TRUE, wenn C ein Element der als Eingabevektor übergebenen Tabelle ist |
VF_iselementV | prüft für jedes Element von X, ob es in der Tabelle Tab enthalten ist |
VI_shl | Bits nach links schieben |
VI_shr | Bits nach rechts schieben |
VI_or | OR-Operation mit Bit-Maske |
VI_xor | XOR-Operation mit Bit-Maske |
VI_not | alle Bits invertieren |
VF_neg | Yi = - Xi |
VF_abs | Yi = | Xi | |
VCF_conj | Yi.Re = Xi.Re; Yi.Im = −(Xi.Re) |
VF_inv | Yi = 1.0 / Xi |
|
|
Neben diesen Grundfunktionen sind auch häufig gebrauchte Kombinationen von Addition und Division sowie die Pythagoras-Formel aufgenommen:
|
|
Die Funktionen in der rechten Kolumne der obigen beiden Abschnitte existieren zusätzlich noch in erweiterter Form. Diese wird durch durch das Präfix "VFx_" angegeben (das "x" stammt aus der englischen Bezeichnung "expanded"). Hier wird die jeweilige Funktion nicht für Xi selbst, sondern für (a * Xi + b) berechnet, z.B.
VFx_addV | Zi = (a * Xi + b) + Yi |
VFx_divrV | Zi = Yi / (a * Xi + b) |
Für die vier Grundrechenarten existiert noch eine weitere Spezialform, bei der das Ergebnis mit einem konstanten Faktor multipliziert wird. Diese "skalierte" Version wird durch den zusätzlichen Buchstaben "s" im Präfix angegeben:
VFs_addV | Zi = C * (Xi + Yi) |
VFs_subV | Zi = C * (Xi - Yi) |
VFs_mulV | Zi = C * (Xi * Yi) |
VFs_divV | Zi = C * (Xi / Yi) |
Unter den weiteren einfachen arithmetischen Operationen seien genannt:
VF_maxC | setzt Yi gleich Xi oder C, je nachdem, welcher von beiden Werten der größere ist |
VF_minC | wählt den kleineren Wert aus Xi und C |
VF_maxV | wählt den größeren Wert aus Xi und Yi und speichert ihn als Zi |
VF_minV | wählt den kleineren Wert aus Xi und Yi und speichert ihn als Zi |
VF_limit | führt eine Begrenzung des Zahlenbereiches durch |
VF_flush0 | setzt alle Werte unterhalb eines als Parameter übergebenen Absolut-Schwellenwertes gleich 0 |
VF_flushInv | berechnet das Inverse aller Werte oberhalb eines als Parameter übergebenen Absolut-Schwellenwertes und setzt das Ergebnis für alle anderen gleich 0 |
VF_absHuge | ersetzt negative Polstellen durch positive |
VF_intfrac | spaltet Zahlen in ihre Ganzzahl- und Bruchzahl-Anteile auf |
VF_mantexp | trennt Zahlen in Mantisse und Exponent |
Während allgemein alle OptiVec-Funktionen Ein- und Ausgabe-Vektoren desselben Datentyps verarbeiten, existieren die arithmetischen Funktionen auch für "gemischte" Operationen zwischen Fließkomma- und Ganzzahltypen, wobei das Ergenis stets in dem Fließkomma-Typ gespeichert wird, z.B.:
VF_addVI | fVector Z = fVector X + iVector Y |
VD_mulVUL | dVector Z = dVector X * ulVector Y |
VE_divrVBI | eVector Z = biVector Y / eVector X |
In ähnlicher Weise existieren Funktionen für die Akkumulation von Daten in entweder demselben oder in höher-genauen Datentypen. Diese Funktionen entsprechen der Operation Y += X. Beispiele sind:
VF_accV | fVector Y += fVector X |
VD_accVF | dVector Y += fVector X |
VF_accVI | fVector Y += iVector X |
VQI_accVLI | qiVector Y += liVector X |
Innerhalb der Fließkomma-Datentypen besteht zusätzlich die Möglichkeit, gleich zwei Vektoren auf einmal zu akkumulieren:
VF_acc2V | fVector Y += fVector X1 + fVector X2 |
VD_acc2VF | dVector Y += fVector X1 + fVector X2 |
Ebenfalls nur innerhalb der Fließkomma-Datentypen existieren Funktionen zur Akkumulation von Quadraten und Produkten:
VF_accV2 | fVector Y += fVector X2 |
VD_accVF2 | dVector Y += fVector X2 |
VCF_accVmulVconj | cfVector Y += cfVector X * cfVector Y* |
VF_scalprod | Skalarprodukt zweier Vektoren |
VF_xprod | Vektor- oder Kreuzprodukt zweier Vektoren |
VF_Euclid | Euclid'sche Norm eines Vectors |
Werden andererseits die Koordinaten mehrerer Punkte in einer Ebene entweder in zwei getrennten Vektoren X und Y oder in einem komplexen Vektor gespeichert, so existiert eine Funktion zur Rotation dieser Koordinaten:
VF_rotateCoordinates | Rotation der durch zwei Vektoren X und Y angegebenen Koordinaten mehrer Punkte in einer Ebene gegen den Uhrzeigersinn; die neuen Koordinaten werden als Xrot und Yrot gespeichert. |
VCF_rotateCoordinates | Rotation der durch Real- und Imaginärteil des komplexen Vektors XY angegebenen Koordinaten mehrer Punkte in einer Ebene gegen den Uhrzeigersinn; die neuen Koordinaten werden als Real- und Imaginärteil des komplexen Vektors XYrot gespeichert. |
Normalversion | ungeschützte Version | Operation |
VF_square | VFu_square | Quadrat |
VF_cubic | VFu_cubic | Kubik |
VF_quartic | VFu_quartic | vierte Potenz |
VF_rsquare | VFu_rsquare | Reziprokes Quadrat |
VF_rcubic | VFu_rcubic | Reziprokes Kubik |
VF_rquartic | VFu_rquartic | Reziprokes der vierten Potenz |
VF_sqrt | VFu_sqrt | Quadratwurzel (entspricht Potenz 0.5) |
VF_rsqrt | ||
VFu_rsqrt | Reziproke Quadratwurzel (entspricht Potenz −0.5) | |
VF_ipow | VFu_ipow | beliebige ganzzahlige Potenzen |
VF_inv | ||
VFu_inv | Reziprok-Wert (entspricht Potenz −1) | |
VF_pow | n.a. | beliebige reelle Potenzen |
VF_powexp | n.a. | reelle Potenzen, multipliziert mit Exponentialfunktion: xrexp(x) |
VF_poly | VFu_poly | Polynom |
VF_polyOdd | VFu_polyOdd | nur aus ungeraden Termen bestehendes Polynom |
VF_polyEven | VFu_polyEven | nur aus geraden Termen bestehendes Polynom |
VF_ratio | VFu_ratio | Quotient zweier Polynome |
VF_ratioOddEven | VFu_ratioOddEven | Quotient zweier Polynome, wobei das Zähler-Polynom nur aus ungeraden Termen besteht und das Nenner-Polynom ausschließlich gerade Terme aufweist (wie z.B. bei der Darstellung der Tangens-Funktion als Quotient der Taylor-Reihen von Sinus und Cosinus) |
VF_ratioEvenOdd | VFu_ratioEvenOdd | Quotient zweier Polynome, wobei das Zähler-Polynom nur aus geraden Termen besteht und das Nenner-Polynom ausschließlich ungerade Terme aufweist (wie z.B. bei der Darstellung der Cotangens-Funktion) |
Während bei allen eben genannten Funktionen spezifizierte Potenzen beliebiger Zahlen erhalten werden, ist es bei der nun folgenden Gruppe von Funktionen umgekehrt:
VF_pow10 | reelle Potenzen von 10 |
VF_ipow10 | ganzzahlige Potenzen von 10 (als Fließkomma-Zahlen gespeichert) |
VF_pow2 | reelle Potenzen von 2 |
VF_ipow2 | ganzzahlige Potenzen von 2 (als Fließkomma-Zahlen gespeichert) |
VF_exp | Exponentialfunktion |
VF_exp10 | Exponentialfunktion zur Basis 10 (identisch mit VF_pow10) |
VF_exp2 | Exponentialfunktion zur Basis 10 (identisch mit VF_pow2) |
VF_expArbBase | Exponentialfunktion zu beliebiger Basis |
Die äquivalenten Funktionen für komplex-zahlige Vektoren sind ebenfalls vorhanden, sowohl für cartesische als auch in Polarkoordinaten. Zusätzlich werden zwei Spezialfälle behandelt:
VCF_powReExpo | reelle Potenzen komplexer Zahlen |
VCF_exptoP | übernimmt einen cartesischen Eingabevektor und gibt dessen Exponentialfunktion in Polarkoordinaten zurück |
VF_exp | Exponentialfunktion |
VF_expc | komplementäre Exponentialfunktion Yi = 1 - exp[ Xi ] |
VF_expmx2 | Exponentialfunktion des negativen Quadrates des Arguments: Yi = exp[ - X2i ].
Dies ist eine glockenförmige Funktion ähnlich der Gauss-Funktion |
VF_Gauss | Gauss'sche Verteilungsfunktion |
VF_erf | Gauss'sches Fehlerintegral |
VF_erfc | Komplement des Gauss'schen Fehlerintegrals, 1 - erf( Xi ) |
VF_powexp | reelle Potenzen, multipliziert mit Exponentialfunktion, Xirexp(Xi) |
VF_sinh | Hyperbel-Sinus |
VF_cosh | Hyperbel-Cosinus |
VF_tanh | Hyperbel-Tangens |
VF_coth | Hyperbel-Cotangens |
VF_sech | Hyperbel-Secans |
VF_cosech | Hyperbel-Cosecans |
VF_sech2 | Quadrat des Hyperbel-Secans |
VF_log10 | dekadischer Logarithmus (zur Basis 10) |
VF_log | natürlicher Logarithmus (zur Basis e) |
VF_ln | Synonym für VF_log |
VF_log2 | binärer Logarithmus (zur Basis 2) |
VPF_log10toC | dekadischer Logarithmus (zur Basis 10) |
VPF_logtoC | natürlicher Logarithmus (zur Basis e) |
VPF_lntoC | Synonym für VPF_logtoC |
VPF_log2toC | binärer Logarithmus (zur Basis 2) |
Als Sonderform des Zehner-Logarithmus ist die Formel für die Optische Dichte, OD = log10( X0/X ) anzusehen. Aufgrund ihrer Bedeutung in der experimentellen naturwissenschaftlichen Arbeit ist sie hier in mehreren Varianten aufgenommen, von denen einige Beispiele in der folgenden Tabelle zusammengefaßt sind:
VF_OD | OD = log10( X0/X ) für fVector als Eingabe und als Ausgabe |
VF_ODwDark | Optische Dichte mit Dunkelstrom-Korrektur: OD = log10( (X0−X0Dark) / (X−XDark) ) für fVector als Eingabe und als Ausgabe |
VUS_ODtoF | OD in float / Single-Genauigkeit für usVector als Eingabe |
VUL_ODtoD | OD in double-Genauigkeit für ulVector als Eingabe |
VQI_ODtoEwDark | OD mit Dunkelstrom-Korrektur in extended-Genauigkeit für qiVector als Eingabe |
VF_sin | Sinus |
VFr_sin | schnelle Sinus-Funktion für den reduzierten Eingabe-Bereich -2p ≤ Xi ≤ +2p |
VF_cos | Cosinus |
VFr_cos | schnelle Cosinus-Funktion für -2p ≤ Xi ≤ +2p |
VF_sincos | gleichzeitige Berechnung von Sinus und Cosinus |
VFr_sincos | Sinus und Cosinus für -2p ≤ Xi ≤ +2p |
VF_tan | Tangens |
VF_cot | Cotangens |
VF_sec | Secans |
VF_cosec | Cosecans |
Die Quadrate der trigonometrischen Funktionen werden gebildet durch:
VF_sin2 | Sinus² |
VFr_sin2 | Sinus² für -2p ≤ Xi ≤ +2p |
VF_cos2 | Cosinus² |
VFr_cos2 | Cosinus² für -2p ≤ Xi ≤ +2p |
VF_sincos2 | Sinus² und Cosinus² gleichzeitig |
VFr_sincos2 | Sinus² und Cosinus² für -2p ≤ Xi ≤ +2p |
VF_tan2 | Tangens² |
VF_cot2 | Cotangens² |
VF_sec2 | Secans² |
VF_cosec2 | Cosecans² |
VF_sinrpi | Sinus von p/q * p |
VF_cosrpi | Cosinus von p/q * p |
VF_sincosrpi | Sinus und Cosinus von p/q * p at once |
VF_tanrpi | Tangens von p/q * p |
VF_cotrpi | Cotangens von p/q * p |
VF_secrpi | Secans von p/q * p |
VF_cosecrpi | Cosecans von p/q * p |
Spezialisierte Versionen gebrauchen Tabellen, um häufig vorkommende Werte direkt zu lesen, anstatt sie berechnen zu müssen. Die werden durch die Suffixe "rpi2" (Vielfache von p dividiert durch eine ganzzahlige Potenz von 2) und "rpi3" (Vielfache von p dividiert durch ein ganzzahliges Vielfaches von 3) bezeichnet. Beispiele sind:
VF_sinrpi2 | Sinus von p / 2n * p |
VF_tanrpi3 | Tangens von p / (3*n) * p |
Zwei spezielle trigonometrische Funktionen sind:
VF_sinc | Sinc-Funktion, Yi = sin( Xi ) / Xi |
VF_Kepler | Kepler-Funktion (zeitabhängige Winkelposition eines Himmelskörpers nach dem Zweiten Keplerschen Gesetz bei gegebener Umlaufzeit und Exzentrizität) |
Vektorisierte inverse trigonometrische Funktionen (die "Arcus"-Funktionen) stehen zur Verfügung als:
VF_asin | Arcus-Sinus |
VF_acos | Arcus-Cosinus |
VF_atan | Arcus-Tangens |
VF_atan2 | Arcus-Tangens von Quotienten, Zi = atan( Yi / Xi ) |
VF_derivV | Ableitung eines Y-Arrays nach einem X-Array |
VF_derivC | desgleichen für konstante Intervalle zwischen den X-Werten |
VF_integralV | Wert des Integrals eines Y-Arrays über einem X-Array |
VF_runintegralV | Punkt-für-Punkt-Integral ("laufendes" Integral) |
VF_integralC | Integral über einer X-Achse mit konstantem Punktabstand |
VF_runintegralC | Punkt-für-Punkt-Integral über einer X-Achse mit konstantem Punktabstand |
VF_ismonoton | Test, ob sich die Elemente eines Vektors in monoton steigender oder fallender Anordnung befinden |
VF_iselementC | Test, ob ein gegebener Wert in einem Vektor auftaucht |
VF_searchC | Bestimmung des einem vorgegebenen Wert C am nächsten kommenden Eintrages in einer Tabelle |
VF_localmaxima | Detektion lokaler Maxima (Punkte, deren rechter und linker Nachbar kleiner sind) |
VF_localminima | Detektion lokaler Minima (Punkte, deren rechter und linker Nachbar größer sind) |
VF_max | globales Maximum |
VF_min | globales Minimum |
VF_minmax | globales Minimum und Maximum |
VF_maxind | globales Maximum und sein Index |
VF_minind | globales Minimum und sein Index |
VF_absmax | globales Absolutwert-Maximum |
VF_absmin | globales Absolutwert-Minimum |
VF_absminmax | globales Absolutwert-Minimum und -Maximum |
VF_minpos | kleinster positiver Wert innerhalb eines Vektors |
VF_absmaxind | global größter Absolutwert und sein Index |
VF_absminind | global kleinster Absolutwert und sein Index |
VF_maxexp | global größter Exponent |
VF_minexp | global kleinster Exponent |
VF_runmax | "laufendes" Maximum |
VF_runmin | "laufendes" Minimum |
Die komplexen Äquivalente der zuletzt genannten Gruppe von Funktionen sind:
VCF_maxReIm | größter Real- und größter Imaginärteil einzeln |
VCF_minReIm | kleinster Real- und kleinster Imaginärteil einzeln |
VCF_absmaxReIm | betragsmäßig größter Real- und Imaginärteil einzeln |
VCF_absminReIm | betragsmäßig kleinster Real- und Imaginärteil einzeln |
VCF_absmax | größter Absolutwert (Zeigerlänge; dies ist eine reelle Zahl) |
VCF_absmin | kleinster Absolutwert (Zeigerlänge) |
VCF_cabsmax | komplexe Zahl mit dem größten Absolutwert |
VCF_cabsmin | komplexe Zahl mit dem kleinsten Absolutwert |
VCF_sabsmax | komplexe Zahl mit der größten Summe |Re| + |Im| |
VCF_sabsmin | komplexe Zahl mit der kleinsten Summe |Re| + |Im| |
VCF_absmaxind | größter Absolutwert und sein Index |
VCF_absminind | kleinster Absolutwert und sein Index |
Summen, Produkte usw. werden durch die in chapter 4.9 als Statistik-Funktionen zusammengefaßten Routinen gebildet.
Zur Berechnung des Schwerpunktes eines Vektors stehen zwei Funktionen zur Verfügung:
VF_centerOfGravityInd | Schwerpunkt eines Vektors in Form eines interpolierten Element-Indexes |
VF_centerOfGravityV | Schwerpunkt eines Y-Vektors bei explizit gegebener X-Achse |
VF_FFTtoC | Schnelle Fourier-Transformation (FFT) eines reellen Vektors; das Ergebnis ist ein cartesisch-komplexer Vektor |
VF_FFT | Vorwärts- und Rückwärts-FFT reeller Vektoren; das Ergebnis der Vorwärts-Transformation ist ein gepackt-komplexer Vektor derselben Speicher-Größe wie der Eingabevektor. |
VCF_FFT | Vorwärts- und Rückwärts-FFT komplexer Vektoren |
MF_Rows_FFT | FFT entlang der Zeilen einer Matrix; diese Funktion kann zur Batch-Verarbeitung mehrer Vektoren gleicher Größe verwendet werden, die hierfür als Zeilen einer Matrix gespeichert werden müssen |
MF_Cols_FFT | FFT entlang der Spalten einer Matrix; diese Funktion kann zur Batch-Verarbeitung mehrer Vektoren gleicher Größe verwendet werden, die hierfür als Spalten einer Matrix gespeichert werden müssen |
VF_convolve VF_convolvewEdit |
Faltung mit einer gegebenen Impulsantwortfunktion |
VF_deconvolve VF_deconvolvewEdit |
Entfaltung unter Annahme einer gegebenen Impulsantwortfunktion |
VF_filter | spektrale Filterung |
VF_spectrum | spektrale Analyse |
VF_xspectrum | Kreuz-Leistungsspektrum zweier Signale (komplexe Version) |
VF_xspectrumAbs | Kreuz-Leistungsspektrum zweier Signale (Absolutwert) |
VF_coherence | Kohärenzfunktion zweier Signale |
VF_autocorr | Autokorrelationsfunktion eines Datensatzes |
VF_xcorr | Kreuzkorrelationsfunktion zweier Datensätze |
VF_setRspEdit | Standard-Editierschwelle für den intermediär bei Faltungen und Entfaltungen berechneten Filter setzen (entscheidet über die Behandlung "verlorener" Frequenzen) |
VF_getRspEdit | derzeit eingestellte Standard-Editierschwelle lesen |
Alle genannten Funktionen benötigen intern zusätzlichen Pufferspeicher, den sie selbst allozieren und wieder freigeben. Um diese Ineffizienz bei wiederholten Aufrufen umgehen zu können, existiert von allen diesen Funktionen eine durch das Präfix VFb_ gekennzeichnete Version, die einen Zeiger auf vom Aufrufer zur Verfügung gestellten Pufferspeicher als zusätzliches Argument übernimmt. Die notwendige Mindestgröße des Pufferspeichers ist in FUNKREF.HTM für jede Funktion einzeln angegeben.
Obwohl sie keine Fourier-Transformation benutzen, sollen in diesem Zusammenhang die Funktionen VF_biquad für bi-quadratische Audio-Filterung sowie VF_smooth (engl. to smooth = glätten) als einer etwas groben Form der Frequenzfilterung genannt werden.
VF_sum | Summe aller Elemente |
VI_fsum | Summe aller Elemente eines Ganzzahl-Vektors, die als Fließkommazahl in double- oder extended- Genauigkeit akkumuliert wird |
VF_prod | Produkt aller Elemente |
VF_ssq | Quadratsumme aller Elemente |
VF_sumabs | Summe der Absolutwerte aller Elemente |
VF_rms | Wurzel des mittleren Quadrats aller Elemente |
VF_runsum | laufende Summe |
VF_runprod | laufendes Produkt |
VF_sumdevC | Summe der Abweichungen von einem Sollwert, Summe( |Xi-C| ) |
VF_sumdevV | Summe der Abweichungen von einem anderen Vektor, Summe( |Xi-Yi| ) |
VF_sumdevVwSaturation | Summe der Abweichungen von einem anderen Vektor, Summe( |Xi-Yi| ) mit Begrenzung eventuellen Überlaufs auf HUGE_VAL |
VF_subV_sumabs | Differenz zweier Vektoren und Summe über die Absolutwerte der einzelnen Abweichungen |
VF_avdevC | mittlere Abweichung von einem Sollwert, 1/N * Summe( |Xi-C| ) |
VF_avdevV | mittlere Abweichung von einem anderen Vektor, 1/N * Summe( |Xi-Yi| ) |
VF_ssqdevC | Quadratsumme der Abweichungen von einem Sollwert, Summe( (Xi - C)² ) |
VF_ssqdevV | Quadratsumme der Abweichungen von einem anderen Vektor, Summe( (Xi - Yi)² ) |
VF_ssqdevVwSaturation | Quadratsumme der Abweichungen von einem anderen Vektor, Summe( (Xi - Yi)² ) mit Begrenzung eventuellen Überlaufs auf HUGE_VAL |
VF_subV_ssq | Differenz zweier Vektoren und Quadratsumme über die einzelnen Abweichungen |
VF_chi2 | Chi-Quadrat-Testwert |
VF_chi2wSaturation | Chi-Quadrat-Testwert mit Begrenzung eventuellen Überlaufs auf HUGE_VAL |
VF_subV_chi2 | Differenz zweier Vektoren und Chi-Quadrat-Testwert |
VF_chiabs | "robuster" Testwert ähnlich VF_chi2, aber auf absoluten statt auf quadratischen Abweichungen basierend |
VF_chiabswSaturation | wie VF_chiabs, aber mit Begrenzung eventuellen Überlaufs auf HUGE_VAL |
VF_subV_chiabs | Differenz zweier Vektoren und chiabs-Testwert |
VF_mean | gleichgewichtetes Mittel (Durchschnitt) aller Elemente |
VF_meanwW | gewichtetes Mittel |
VF_meanabs | gleichgewichtetes Mittel aller Absolutwerte |
VF_selected_mean | Mittel über diejenigen Vektorelemente, die in einen bestimmten Wertebereich fallen; hierdurch lassen sich Ausreißerpunkte von der Mittelwertbildung ausschließen |
VF_varianceC | Varianz einer Verteilung bezüglich eines Sollwertes |
VF_varianceCwW | desgl. mit Einzelpunkt-Wichtung |
VF_varianceV | Varianz einer Verteilung bezüglich einer zweiten |
VF_varianceVwW | desgl. mit Einzelpunkt-Wichtung |
VF_meanvar | Mittelwert und Varianz einer Verteilung |
VF_meanvarwW | desgl. mit Einzelpunkt-Wichtung |
VF_median | Median einer Verteilung |
VF_corrcoeff | linearer Korrelationskoeffizient zweier Verteilungen |
VF_distribution | Histogramm (diskrete eindimensionale Verteilungsfunktion einer eindimensionalen Verteilung) |
VF_min_max_mean_stddev | gleichzeitige Berechnung von Minimum, Maximum, Mittelwert und Standardabweichung einer eindimensionalen Verteilung |
Eine detaillierte Beschreibung der verschiedenen Konzepte zur Datenanpassung an die unterschiedlichen Klassen von Modellfunktionen wird in Kap. 13 von MATRIXD.HTM gegeben. Daher genüge an dieser Stelle eine tabellarische Zusammenfassung der vorhandenen X-Y-Anpassungsroutinen:
VF_linregress | lineare Regression von X-Y-Daten mit gleicher Wichtung aller Datenpunkte |
VF_linregresswW | dasselbe mit ungleicher Wichtung |
VF_polyfit | Koeffizienten eines Polynoms an vorhandene X-Y-Daten anpassen |
VF_polyfitwW | dasselbe mit ungleicher Wichtung der Datenpunkte |
VF_polyfitOdd | Koeffizienten eines nur aus ungeraden Termen bestehenden Polynoms an vorhandene X-Y-Daten anpassen |
VF_polyfitOddwW | dasselbe mit ungleicher Wichtung der Datenpunkte |
VF_polyfitEven | Koeffizienten eines nur aus geraden Termen bestehenden Polynoms an vorhandene X-Y-Daten anpassen |
VF_polyfitEvenwW | dasselbe mit ungleicher Wichtung der Datenpunkte |
VF_linfit | Koeffizienten einer beliebigen, in ihren Parametern linearen Funktion an vorhandene X-Y-Daten anpassen |
VF_linfitwW | dasselbe mit ungleicher Wichtung der Datenpunkte |
VF_nonlinfit | Koeffizienten einer beliebigen, möglicherweise nicht-linearen Funktion an vorhandene X-Y-Daten anpassen |
VF_nonlinfitwW | dasselbe mit ungleicher Wichtung der Datenpunkte |
VF_multiLinfit | mehrere X-Y-Datensätze gleichzeitig an eine gemeinsame lineare Funktion anpassen |
VF_multiLinfitwW | dasselbe mit ungleicher Wichtung der Datenpunkte |
VF_multiNonlinfit | mehrere X-Y-Datensätze gleichzeitig an eine gemeinsame nicht-lineare Funktion anpassen |
VF_multiNonlinfitwW | dasselbe mit ungleicher Wichtung der Datenpunkte |
VF_cprint | Windows mit MS Visual C++ oder Borland / Embarcadero Compiler:/u> Ausdruck der Elemente eines Vektors auf dem Bildschirm (der "Konsole"; daher das "c" im Namen) im aktuellen Textfenster, wobei die Seiten umgebrochen werden. Höhe und Breite dieses Fensters werden automatisch detektiert. Nur für Konsolen-Anwendungen.
Andere Windows-Compiler sowie Linux: identisch mit VF_print. |
VF_print | ähnlich VF_cprint; auch hier erfolgt die Ausgabe auf dem Bildschirm, aber ohne automatische Detektion der Bildschirmdaten. Die angenommene Standardbreite wird durch die symbolische Konstante V_consoleWindowWidth angegeben (definiert in <VecLib.h> bzw. in der Unit VecLib als 150). Ein Seitenumbruch findet nicht statt (d.h. alle Seiten laufen durch, bis die letzte schließlich stehenbleibt). (Nur Konsolen-Anwendungen) |
VF_fprint | Ausgabe eines Vektors in einen Stream |
VF_chexprint | ähnlich VF_cprint, aber Ausgabe im Hexadezimal-Format. |
VF_hexprint | ähnlich VF_print, aber Ausgabe im Hexadezimal-Format. |
VF_fhexprint | ähnlich VF_fprint, aber Ausgabe im Hexadezimal-Format. |
VF_write | Ablage von Daten im ASCII-Format auf Festplatte |
VF_read | liest einen Vektor aus einer ASCII-Datei ein |
VF_nwrite | schreibt n gleichartige Vektoren als Spalten einer Tabelle in eine ASCII-Datei |
VF_nread | liest die Spalten einer Tabelle in n gleichartige Vektoren ein |
VF_store | Speichern im Binärformat |
VF_recall | Einlesen im Binärformat |
Die folgenden Funktionen modifizieren die Standardeinstellung für VF_write, VF_nwrite und VI_read:
VF_setWriteFormat | bestimmtes Ausgabeformat vorgeben |
VF_setWriteSeparate | Trennzeichen zwischen aufeinander folgenden Element für VF_write vorgeben |
VF_setNWriteSeparate | Trennzeichen zwischen den durch VF_nwrite geschriebenen Spalten definieren |
V_setRadix | für die ganzzahligen V.._read-Funktionen eine andere als die als Standard angenommene Basis 10 einstellen. |
V_initPlot | Graphikfunktionen von VectorLib initialisieren. Es ist nicht nötig, nach Gebrauch einen Abschluss durchzuführen, da V_initPlot nur Zahlenwerte initialisiert, aber keine Speicherreservierungen o.ä. durchführt und die Windows-Graphikfunktionen stets vorhanden bleiben. V_initPlot reserviert automatisch einen Bildschirmausschnitt für Plot-Operationen, der etwa die rechten 2/3 des Bildschirms umfaßt und oben noch Raum für eine Überschrift sowie unten ein paar Zeilen für eine Unterschrift freilässt. Am unteren Rand bleiben einige Zeilen frei. Um die Standard-Einstellung zu ändern, rufe man V_setPlotRegion nach V_initPlot. |
V_initPrint | Graphikfunktionen von VectorLib initialisieren und die Ausgabe auf einen Drucker umleiten. Standardmäßig wird eine ganze Seite zum Druck verwandt. Auch diese Einstellung läßt sich mit Hilfe von V_setPlotRegion nach V_initPrint ändern. |
V_setPlotRegion | Definition eines von der automatischen Wahl abweichenden Fensterausschnitts |
VectorLib unterscheidet zwei Arten von Plot-Funktionen: AutoPlot und DataPlot. Alle AutoPlot-Funktionen (z.B. VF_xyAutoPlot) führen die folgenden Schritte durch:
V?_autoPlot (kein Suffix): | sowohl X-Achse als auch Y-Achse linear. |
V?_autoPlot_xlg_ylin: | X-Achse logarithmisch, Y-Achse linear. |
V?_autoPlot_xlg_ylg: | sowohl X-Achse also auch Y-Achse logarithmisch. |
V?_autoPlot_xlin_ylg: | X-Achse linear, Y-Achse logarithmisch. |
VF_xyAutoPlot | automatisch skalierter Plot eines X-Y-Vektor-Paares |
VF_yAutoPlot | automatisch skalierter Plot eines Y-Vektors gegen den als X-Achse verwendeten Element-Index |
VF_xy2AutoPlot | gleichzeitige Auftragung zweier X-Y-Paare, wobei die Achsenskalierung sicherstellt, dass beide in dasselbe Koordinatensystem passen |
VF_y2AutoPlot | desgl. für zwei gegen ihre Indizes aufgetragene Y-Vektoren |
VF_xyDataPlot | zusätzlichen X-Y-Datensatz auftragen |
VF_yDataPlot | zusätzlichen Y-Vektor gegen seinen Index auftragen |
Vektoren aus cartesisch-komplexen Zahlen werden in der komplexen Ebene dargestellt, d.h. die Imaginärteile gegen die Realteile aufgetragen. Hierzu dienen
VCF_autoPlot | Auftragung eines cartesisch-komplexen Vektors |
VCF_2AutoPlot | gleichzeitige Auftragung zweier komplexer Vektoren |
VCF_dataPlot | Hinzufügung eines weiteren komplexen Vektors zu einer bestehenden Auftragung |
Funktionen zur Auftragung polar-komplexer Vektoren sind derzeit nicht enthalten.
Es ist möglich, mehrere Koordinatensysteme in ein-und-dasselbe Fenster zu zeichnen. Die Position jedes Koordinatensystems muss über die oben erwähnte Funktion V_setPlotRegion spezifiziert werden. Dem Umschalten zwischen ver-
schiedenen Koordinatensystemen (z.B. um neue DataPlots hinzuzufügen) dienen
die folgenden Funktionen:
V_continuePlot | Wiederherstellung des zuletzt für einen Plot verwendeten Viewports und der zugehörigen Achsen-Skalierungen |
V_getCoordSystem | Speichern der Position und Skalierungen eines Koordinatensystems |
V_setCoordSystem | Wiederherstellung von Position und Skalierungen eines Koordinatensystems. Diese müssen zuvor mittels V_getCoordSystem gespeichert worden sein. |
Die Produktions-Bibliotheken von OptiVec behandeln alle mathematischen Fehler (Überlauf, Singularitäten, Bereichsfehler, Genauigkeits-Verlust) "stillschweigend" und setzen die Programmausführung mit einem wenn möglich in den darstellbaren Bereich hinein korrigierten Ergebnis fort.
Die Debug-Bibliotheken zeigen Fehler zusätzlich durch Ausgabe einer Meldung an. Wie unten noch näher erklärt, kann mit Hilfe der Funktion V_setFPErrorHandling eingestellt werden, welche Fehler tatsächlich gemeldet werden. Die Funktion V_setErrorEventFile wiederum erlaubt die Auswahl zwischen Meldungs-Ausgabe in eine Datei, ein Popup-Window oder ein Text-Fenster.
Eine Folge identischer Fehler innerhalb ein- und derselben OptiVec-Funktion führt immer nur zu einer einzigen Meldung. Nachfolgende identische Meldungen werden unterdrückt.
Dem Abfangen und Behandeln von Fehlern sind allerdings Grenzen gesetzt, insbesondere für den Überlauf-Schutz innerhalb der extended-, teilweise auch der double-Versionen von OptiVec-Funktionen. Der Programmierer sollte generell darauf achten, dass konstante Paramater bei der Übergabe an Funktionen sicherheitshalber nicht größer als 1.E32 für float / Single, 1.E150 für double und 1.E2000 für extended sind.
In den "erweiterten" Versionen aller extended-genauen Funktionen (also denen mit den Präfixen VEx_ und VCEx_; z.B. VEx_exp) wird generell kein Überlauf-Schutz für die Berechnung des Zwischenergebnisses A*Xi+B gewährt, sondern nur für den Kern der Funktion selbst und für die abschließende Multiplikation mit dem Skalierungsfaktor C.
Zwischen Fließkomma- und Ganzzahlen besteht ein grundsätzlicher Unterschied bezüglich OVERFLOW- und DOMAIN- Fehlern: Für Fließkommazahlen sind dies ernst zu nehmende (OVERFLOW) bzw. zur Unbrauchbarkeit des Ergebnisses führende (DOMAIN) Fehler. Demgegenüber wird für Ganzzahlen eine implizite modulo-2n-Arithmetik verwendet, bei der diese "Fehler" gar nicht als solche betrachtet werden. Im Gegenteil: Häufig wird sogar der Überlauf als schnelles Mittel der modulo-Division ausgenutzt. In den folgenden beiden Abschnitten wird die Fehlerbehandlung von Fließkomma- und Ganzzahlen detailliert dargestellt.
Nur 32-bit-Versionen (also nicht für 64-bit):
Alle Funktionen, in denen INTEGER OVERFLOW (z.B. in VI_ramp, VI_mulV, etc.) oder INTEGER DOMAIN-Fehler (z.B. in V_ItoU für negative X-Werte) auftreten können, existieren in zwei Varianten: Die Normalversion wendet implizite modulo-2n-Arithmetik an und wandelt vorzeichenbehaftete und vorzeichenlose Typen gemäß ihrem Bitmuster ineinander um. Für die 16-bit- und 32-bit-Ganzzahltypen (nicht aber für 8-bit und 64-bit) gibt es eine zweite Version, die zwar auch modulo-2n-Arithmetik anwendet, Fehler aber detektiert. Diese zweite Variante wird durch den Buchstaben "o" (für "OVERFLOW-Detektion") im Präfix angegeben: VIo_, VSIo_, VULo_, usw. Für die Ganzzahl-Datentypumwandlungsfunktionen wird das Präfix V_ zu Vo_ erweitert. Was im Falle eines Fehlers zu geschehen hat, wird durch einen Aufruf der Funktion V_setIntErrorHandling festgelegt. Diese benötigt ein Argument des Typs V_ihand (definiert in <VecLib.h> bzw. in der Unit VecLib), das drei verschiedene Werte annehmen kann:
ierrNote | Ausgabe einer Fehlermeldung und Fortsetzung des Programmes |
ierrAbort | Ausgabe einer Fehlermeldung und Abbruch des Programmes |
ierrIgnore | Fehler ignorieren; mit dieser Option läßt sich die Fehlerbehandlung für einzelne Stellen im Programm wieder ausschalten. |
Obwohl ein Aufruf von
V_setIntErrorHandling( ierrIgnore );
im Prinzip verwedet werden kann, um die Fehlerbehandlung auszuschalten, ist es stets besser, einfach die Normalversion (Präfix VI_) anstelle der VIo_-Version mit überbrückter Fehlerbehandlung zu verwenden, da die Normalversion immer um ein Vielfaches schneller ist.
nur C/C++:
Um die Überlauf-abfangede Variante nicht nur für einzelne Funktionsaufrufe, sondern überall zu verwenden, ist der einfachste Weg, die symbolische Konstante V_trapIntError im Programmkopf vor(!) Einschluß von <VecLib.h> zu definieren, z.B.
#define V_trapIntError 1
#include <VIstd.h>
#include <VImath.h>
.....
main() /* oder WinMain() oder OwlMain() */
{
siVector SI1, SI2;
I1 = VSI_vector( 1000 ); SI2 = VSI_vector( 1000 );
V_setIntErrorHandling( ierrNote );
VSI_ramp( SI1, 1000, 0, 50 ); /* hier wird ein Überlauf auftreten! */
V_setIntErrorHandling( ierrIgnore );
VSI_mulC( SI2, SI1, 1000, 5 );
/* hier entsteht zwar eine ganze Serie von Überläufen, die aber alle ignoriert werden */
....
}
Für jede Funktion werden die möglichen Fehlerarten, die abgefangen und behandelt werden, in der Funktions-Referenz aufgeführt. Alle von mathematischen ANSI C- oder Pascal/Delphi-Funktionen abgeleiteten VectorLib-Funktionen (also diejenigen, deren Deklaration sich in <V?math.h> bzw. den Units V?math findet) führen eine vollständige Fehlerbehandlung für jedes Vektorelement durch. Über diese Fehlerbehandlung "pro Element" hinaus wird mit Hilfe des Rückgabewertes angegeben, ob insgesamt irgendein Fehler auftrat oder nicht. Ein Rückgabewert von FALSE (0) bedeutet Fehlerfreiheit; Rückgabewerte von TRUE (ungleich 0) zeigen aufgetretene (und behandelte) Fehler an.
Nur für Debug-Bibliotheken: Wie oben bereits erwähnt, kann durch Aufruf von V_setFPErrorHandling eingestellt werden, welche Fehlerarten zu einer Meldung und welche ggf. zu einem Programm-Abbruch führen. Die vorhandenen Optionen werden durch vordefinierte Konstanten fperrXXX gesetzt:
Konstante | Bedeutung |
fperrIgnore | Sämtliche Fließkommafehler stillschweigend behandeln |
fperrNoteDOMAIN | Bereichsfehler melden |
fperrAbortDOMAIN | Meldung und Programmabbruch bei Bereichsfehlern |
fperrNoteSING | Singularitäten (meist Divisionen durch 0) melden |
fperrAbortSING | Meldung und Programmabbruch bei Singularitäten |
fperrNoteOVERFLOW | Überlauf melden |
fperrAbortOVERFLOW | Meldung und Programmabbruch bei Überlauf |
fperrNoteTLOSS | Totalen Genauigkeitsverlust (z.B. bei sin(1.e30)) melden |
fperrAbortTLOSS | Meldung und Programmabbruch bei Totalem Genauigkeitsverlust |
fperrDefault | Standardeinstellung = fperrAbortDOMAIN + fperrNoteSING + fperrNoteOVERFLOW |
Bei der nun folgenden Beschreibung der einzelnen möglichen Fehler wird als "HUGE_VAL" der größte im jeweiligen Datentyp darstellbare Wert bezeichnet, also MAX_FLT, MAX_DBL oder MAX_LDBL, je nach Typ. Ähnlich wird mit "TINY_VAL" der kleinste eben noch darstellbare Wert ungleich 0 bezeichnet. Dies ist nicht dasselbe wie MIN_VAL, welches der kleinste mit voller Genauigkeit darstellbare Wert ist.
Bei der Bildung des Kehrwertes allerdings folgt ein Überlauf, und hier wird die etwas akademische Unterscheidung zwischen SING- und OVERFLOW-Fehlern fallengelassen und ein SING-Fehler angezeigt, als ob es sich um eine Division um exakt 0 handelte.
Andererseits geben Denormals als Argumente von einigen Funktionen, beispielsweise log, völlig vernünftige Ergebnisse, während exakt 0 als Argument zu einem SING-Fehler führt. Leider behandeln die meisten Compiler auch für diese Funktionen Denormals als 0. Wir schließen uns dem nicht an und berechnen lieber das Ergebnis.
Man mag dies umgehen wollen. Eine einfache Lösung dieses Problems besteht in der Funktion V_setErrorEventFile. Diese Funktion benötigt als Argumente den Namen der gewünschten Ereignis-Datei ("Log-File") und einen Schalter, ScreenAndFile, der darüber entscheidet, ob Fehlermeldungen ausschließlich in die Datei (ScreenAndFile = 0) oder außerdem noch in einer Meldungs-Box (ScreenAndFile = 1) bzw. bei Console-Programmen auch auf dem Bildschirm (ScreenAndFile = 2) auszugeben sind.
Beispiel:
V_setErrorEventFile( "MyLogFil.TXT", 0 ); /* C/C++ */
V_setErrorEventFile( 'MyLogFil.TXT', 0 ); (* Pascal/Delphi *)
Hier werden alle folgenden Fehlermeldungen nur in das Log-File MyLogFil.TXT geschrieben, aber keine auf dem Bildschirm angezeigt.
Durch Aufruf von V_setErrorEventFile( "NULL", 0 ) (C/C++) bzw. V_setErrorEventFile( 'nil', 0 ) (Pascal/Delphi) lassen sich sogar jegliche OptiVec-Meldungen vollständig unterdrücken (so sinnvoll dies auch immer sein mag).
Die Ausgabe in die Log-Datei wird beendet durch V_closeErrorEventFile. Diese letztere Funktion hat keine Argumente und gibt auch nichts zurück. Anschließend erfolgt die Ausgabe eventueller Meldungen entsprechend dem bei Erzeugung der Log-Datei angegebenen Wert von ScreenAndFile.
Man beachte, dass die beschriebene Umleitung von Fehlermeldungen nur für Fehler gilt, die innerhalb von OptiVec-Funktionen auftreten. Es steht dem Nutzer aber frei, eigene Fehlermeldungen mit der von OptiVec benutzten Funktion V_printErrorMsg auszugeben.
Einige Konfigurationen der von OptiVec unterstützten Compiler erlauben nicht den vollen Umfang der oben angegebenen Optionen. Bei manchen fehlt entweder die Ausgabe in eine Message-Box oder die Ausgabe auf den Konsolen-Bildschirm. In diesen Fällen wird ggf. eine Fehlermeldung ausgegeben und die Ausgabe auf die jeweils andere Option umgeleitet.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Obwohl OptiVec gründlich getestet worden ist, besteht natürlich immer die Möglichkeit, dass ein Problem unserer Aufmerksamkeit entgangen ist. Sollten Sie das Gefühl haben, auf ein solches Problem zu stoßen, so versuchen Sie doch bitte, die zu seinem Auftauchen führenden Bedingungen so genau wie möglich zu spezifizieren, und lassen Sie es uns unter support@optivec.com wissen!
Nur C/C++: Man deklariere den Gebrauch von OptiVec-Funktionen mit #include-Anweisungen. Falls Sie MFC (Microsoft Foundation Classes) oder Borlands uralte OWL (ObjectWindows Library) verwenden, müssen die MFC- oder OWL-Include-Dateien vor(!) denen von OptiVec eingeschlossen werden.
Nur Pascal/Delphi: Man deklariere den Gebrauch von OptiVec-Funktionen mit der uses-Anweisung.
Include-Datei (Suffix .H) oder Unit | Inhalt |
VecLib | grundlegenden Definitionen der Datentypen und die Prototypen der allen Datentypen gemeinsamen Funktionen (das sind diejenigen mit dem Präfix V_) mit Ausnahme der Graphik-Routinen |
VFstd, VDstd, VEstd | "Standard-Operationen" für Fließkomma-Zahlen: Erzeugung und Initialisierung von Vektoren, Index-orientierte Manipulationen, Datentyp-Umwandlungen, I/O-Operationen, Statistik, Analysis, geometrische Vektor-Arithmetik und alle auf der Fourier-Transformation basierenden Funktionen. |
VCFstd, VCDstd, VCEstd, VPFstd, VPDstd, VPEstd | Standard-Operationen für cartesisch- und polar-komplexe Vektoren |
VIstd, VBIstd, VSIstd, VLIstd, VQIstd | Standard-Operationen für Ganzzahl-Vektoren mit Vorzeichen |
VUstd, VUBstd, VUSstd, VULstd, VUQstd, VUIstd | Standard-Operationen für vorzeichenlose Ganzzahl-Vektoren |
VFmath, VDmath, VEmath | Algebraische, arithmetische und mathematische Funktionen für Fließkomma-Vektoren |
VCFmath, VCDmath, VCEmath, VPFmath, VPDmath, VPEmath | Arithmetische und mathematische Funktionen für komplexe Vektoren |
VImath, VBImath, VSImath, VLImath, VQImath | Arithmetische und mathematische Funktionen für vorzeichenbehaftete Ganzzahl-Vektoren |
VUmath, VUBmath, VUSmath, VULmath, VUQmath, VUImath | Arithmetische und mathematische Funktionen für vorzeichenlose Ganzzahl-Vektoren |
Vgraph | Graphik-Funktionen für alle Datentypen |
VFNLFIT, VDNLFIT, VENLFIT | Nicht-lineare Datenanpassungs-Funktionen (nur Pascal/Delphi; in C/C++ befinden sie sich in M?std) |
VFMNLFIT, VDMNLFIT, VEMNLFIT | Nicht-lineare Datenanpassungs-Funktionen für Mehrfach-Datensätze (nur Pascal/Delphi; in C/C++ befinden sie sich in M?std) |
MFstd, MDstd, MEstd | Matrix-Operationen für reelle Matrizen |
MCFstd, MCDstd, MCEstd | Matrix-Operationen für cartesisch-komplexe Matrizen |
Mgraph | graphische Darstellung von Matrizen aller Datentypen |
MFNLFIT, MDNLFIT, MENLFIT | Nicht-lineare Datenanpassungs-Funktionen für Datensätze Z = f(X, Y) (nur Pascal/Delphi; in C/C++ befinden sie sich in M?std) |
MFMNLFIT, MDMNLFIT, MEMNLFIT | Nicht-lineare Datenanpassungs-Funktionen für Mehrfach-Datensätze Z = f(X, Y) (nur Pascal/Delphi; in C/C++ befinden sie sich in M?std) |
NEWCPLX | komplexe Klassenbibliothek CMATH; nur C++ |
CMATH | komplexe Bibliothek CMATH für Pascal/Delphi und C |
CFMATH, CDMATH, CEMATH | nur C/C++: typenspezifische Teile von CMATH |
OVXMATH | Einige skalare mathematische Funktionen, die intern von anderen OptiVec-Funktionen benötigt werden; sie stehen allgemein für Anwendungen zur Verfügung (siehe Kap. 9). C/C++: die Sinus-, Cosecans- und Tangens-Tabellen für VF_sinrpi2 etc. befinden sich ebenfalls hier. |
FSINTAB2, DSINTAB2, ESINTAB3, FSINTAB3, DSINTAB3, ESINTAB3 | Sinus-Tabellen (nur Pascal/Delphi; für C/C++ befinden sie sich in OVXMATH) |
FCSCTAB2, DCSCTAB2, ECSCTAB3, FCSCTAB3, DCSCTAB3, ECSCTAB3 | Cosecans-Tabellen (nur Pascal/Delphi; für C/C++ befinden sie sich in OVXMATH) |
FTANTAB2, DTANTAB2, ETANTAB3, FTANTAB3, DTANTAB3, ETANTAB3 | Tangens-Tabellen (nur Pascal/Delphi; für C/C++ befinden sie sich in OVXMATH) |
VecObj | grundlegende Definitionen für VecObj, das Objekt-orientierte Interface für C++ |
fVecObj, dVecObj, eVecObj | Member-Funktionen von VecObj für reelle Vektor-Objekte (nur C++) |
cfVecObj, cdVecObj, ceVecObj pfVecObj, pdVecObj, peVecObj | Member-Funktionen von VecObj für komplexe Vektor-Objekte (nur C++) |
iVecObj, biVecObj, siVecObj, liVecObj, qiVecObj | Member-Funktionen von VecObj für vorzeichenbehaftete Ganzzahl-Vektor-Objekte (nur C++) |
uVecObj, ubVecObj, usVecObj, ulVecObj, uiVecObj | Member-Funktionen von VecObj für vorzeichenlose Ganzzahl-Vektor-Objekte (nur C++) |
OptiVec | schließt das gesamte OptiVec-Paket ein (nur C++) |
VecAll | schließt alle VectorLib- und CMATH-Funktionen ein (nur C oder C++) |
MatAll | schließt alle MatrixLib-Funktionen ein (nur C oder C++) |