Fortran: Druckversion
EinleitungBearbeiten
Fortran ist eine Programmiersprache, die insbesondere für numerische Berechnungen eingesetzt wird. Der Name entstand aus FORmula TRANslation und wurde bis zur Version FORTRAN 77 mit Großbuchstaben geschrieben.
GeschichteBearbeiten
Fortran gilt als die erste jemals tatsächlich realisierte höhere Programmiersprache. Sie geht zurück auf einen Vorschlag, den John W. Backus, Programmierer bei IBM, 1953 seinen Vorgesetzten unterbreitete.
Dem Entwurf der Sprache folgte die Entwicklung eines Compilers durch ein IBM-Team unter Leitung von John W. Backus. Das Projekt begann 1954 und war ursprünglich auf sechs Monate ausgelegt. Tatsächlich konnte Harlan Herrick, der Erfinder der später heftig kritisierten Goto-Anweisung, am 20. September 1954 das erste Fortran-Programm ausführen. Doch erst 1957 wurde der Compiler für marktreif befunden und mit jedem IBM 704-System ausgeliefert. Backus hatte darauf bestanden, den Compiler von Anfang an mit der Fähigkeit zu Optimierungen auszustatten: er sah voraus, dass sich Fortran nur dann durchsetzen würde, wenn ähnliche Ausführungsgeschwindigkeiten wie mit bisherigen Assembler-Programmen erzielt würden.
VersionenBearbeiten
Fortran wurde mehrmals erweitert. Viele neue Sprachelemente wurden zunächst von einem einzelnen Hersteller eingeführt und später in den internationalen Standard übernommen. Als Versionen folgten aufeinander FORTRAN I, FORTRAN II, FORTRAN IV, FORTRAN 66, FORTRAN 77, Fortran 90, Fortran 95, Fortran 2003, Fortran 2008, Fortran 2018, Fortran 2023. Ab FORTRAN 66 ist Fortran von einer internationalen Organisation standardisiert. Die Fortschreibung der Standards ist ein komplizierter Prozess, der oft wesentlich länger dauert als zunächst angestrebt: Der Nachfolger des 1978 erschienenen Standards FORTRAN 77, der als Fortran 8x bezeichnet wurde, war ursprünglich für das Jahr 1982 geplant, später dann für das Jahr 1985, und wurde schließlich unter der Bezeichnung Fortran 90 erst am 11. April 1991 als neuer Standard und Nachfolger von FORTRAN 77 angenommen.[1]
Im Laufe dieser Erweiterungen wurden zahlreiche Sprachelemente aus neueren Programmiersprachen übernommen. Beruhte früher Fortran-Stil noch ganz auf Goto-Anweisungen, kann man seit FORTRAN 77 uneingeschränkt strukturiert programmieren. Mit Fortran 90 wurde das aus der Lochkartenzeit stammende Zeilenformat freigegeben. Ab Fortran 90 wurden interessante Elemente eingeführt, die auch z.B. in Ada vorhanden sind, beispielsweise optionale Parameter und die Möglichkeit, Prozedurparameter nicht nur über die Position in der Parameterliste zu identifizieren, sondern über ihren Namen.
VariantenBearbeiten
Einige von Fortran abgeleitete Programmiersprachen bzw. Dialekte von Fortran sind beispielsweise Ratfor, F und HPF (High Performance Fortran). Auf Fortran aufgesetzt ist das Finite-Elemente-Programmpaket Nastran.
EigenschaftenBearbeiten
Fortran war und ist für numerische Berechnungen vorgesehen und optimiert. Von Anfang an hatte Fortran den Potenz-Operator **. Dieser ist in vielen anderen Hochsprachen nicht vorhanden. Weiters kennt Fortran einen Datentyp für komplexe Zahlen. Mit Fortran 90 wurden Vektor- und Matrix-Operationen standardisiert. Insbesondere für wissenschaftliche und numerische Berechnungen gibt es in FORTRAN umfangreiche Bibliotheken, die immer noch weit verbreitet sind, auch wenn eine zunehmende Menge an Routinen inzwischen nach C und C++ portiert wurde.
CompilerBearbeiten
Kommerzielle SoftwareBearbeiten
F95-Compiler gibt es für praktisch alle Computer, von Arbeitsplatzrechnern bis zu Supercomputern. Hersteller hierfür sind entweder die Computerhersteller wie z.B. IBM, HP, Intel oder aber spezialisierte Softwarehersteller wie z.B. Absoft, PGI, NAG, Lahey, Silverfrost/Salford. Reine F77-Compiler werden heute zumeist nicht mehr hergestellt, da Fortran 77 fast vollständig im Sprachstandard Fortran 95 enthalten ist.
Manche der oben genannten Compiler sind für Privatanwender bzw. nichtkommerzielle Nutzung kostenlos.
Freie SoftwareBearbeiten
Seit Version 4.0 der GNU Compiler Collection (GCC), die praktisch für alle Plattformen vorhanden ist, enthält diese ein Fortran 95-Frontend (gfortran). Die Version 8.0 unterstützt Fortran 2003 nahezu vollständig und Fortran 2008 zum großen Teil.
ÜbersetzerBearbeiten
Es gibt Programme, wie z.B. f2c, zur automatischen Übersetzung von Fortran in (allerdings kaum lesbares) C.
ReferenzenBearbeiten
- ↑ Vorwort von Michael Metcalf in: W. H. Press, S. A. Teukolsky, W. T. Vetterling, B. P. Flannery: Numerical Recipes in Fortran 90. Cambridge University Press, 1999, ISBN 0-521-57439-0.
Was ist ein Compiler?Bearbeiten
Ein Compiler (auch Kompilierer oder Übersetzer) ist ein Computerprogramm, das ein in einer Quellsprache geschriebenes Programm - genannt Quellprogramm - in ein semantisch äquivalentes Programm einer Zielsprache (Zielprogramm) umwandelt. Üblicherweise handelt es sich dabei um die Übersetzung eines von einem Programmierer in einer Programmiersprache geschriebenen Quelltextes in Assemblersprache, Bytecode oder Maschinensprache. Das Übersetzen eines Quellprogramms in ein Zielprogramm durch einen Compiler wird als Kompilierung oder auch als Übersetzung bezeichnet.
Die Bezeichnung Compiler (engl. to compile: zusammenstellen) ist eigentlich irreführend. Ursprünglich bezeichnete das Wort Compiler Programme, die Unterprogramme zusammenfügen (etwa mit heutigen Linkern vergleichbar). Dies geht an der heutigen Kernaufgabe eines Compilers vorbei.
Verwandt mit einem Compiler ist ein Interpreter, der ein Programm nicht in die Zielsprache übersetzt, sondern Schritt für Schritt direkt ausführt.
Aufbau eines CompilersBearbeiten
Compiler werden in verschiedene Phasen gegliedert, die jeweils verschiedene Teilaufgaben des Compilers übernehmen. Sie werden sequentiell ausgeführt. Im Wesentlichen lassen sich zwei Phasen unterscheiden: das Frontend (auch Analysephase), das den Quelltext analysiert und daraus einen attributierten Syntaxbaum erzeugt, sowie das Backend (auch Synthesephase), das daraus das Zielprogramm erzeugt.
Frontend (auch „Analysephase“)Bearbeiten
Im Frontend wird der Code analysiert, strukturiert und auf Fehler geprüft. Es ist auch selbst wieder in Phasen gegliedert:
Lexikalische AnalyseBearbeiten
Die lexikalische Analyse zerteilt den eingelesenen Quelltext in zusammengehörende Token verschiedener Klassen, z. B. Schlüsselwörter, Bezeichner, Zahlen und Operatoren. Dieser Teil des Compilers heißt Scanner oder Lexer.
Ein Scanner benutzt gelegentlich einen separaten Screener, um Whitespace (also Leerzeichen, Tabulatorzeichen, Zeilenenden) und Kommentare zu überspringen.
Syntaktische AnalyseBearbeiten
Die syntaktische Analyse überprüft, ob der eingelesene Quellcode ein korrektes Programm der zu übersetzenden Quellsprache ist, d. h. der Syntax (Grammatik) der Quellsprache entspricht. Dabei wird die Eingabe in einen Syntaxbaum umgewandelt. Dieser Teil wird auch als Parser bezeichnet.
Semantische AnalyseBearbeiten
Die semantische Analyse überprüft die statische Semantik, also über die syntaktische Analyse hinausgehende Bedingungen an das Programm. Zum Beispiel muss eine Variable in der Regel deklariert worden sein, bevor sie verwendet wird, und Zuweisungen müssen mit kompatiblen (verträglichen) Datentypen erfolgen. Dies kann mit Hilfe von Attributgrammatiken realisiert werden. Dabei werden die Knoten des vom Parser generierten Syntaxbaums mit Attributen versehen, die Informationen enthalten. So kann zum Beispiel eine Liste aller deklarierten Variablen erstellt werden. Die Ausgabe der semantischen Analyse nennt man dann dekorierter oder attributierter Syntaxbaum.
Backend (auch „Synthesephase“)Bearbeiten
Das Backend erzeugt aus dem vom Frontend erstellten attributierten Syntaxbaum den Programmcode der Zielsprache.
ZwischencodeerzeugungBearbeiten
Viele moderne Compiler erzeugen aus dem Syntaxbaum einen Zwischencode, der schon relativ maschinennah sein kann und führen auf diesem Zwischencode z. B. Programmoptimierungen durch. Das bietet sich besonders bei Compilern an, die mehrere Quellsprachen oder verschiedene Zielplattformen unterstützen. Hier kann der Zwischencode auch ein Austauschformat sein.
ProgrammoptimierungBearbeiten
Der Zwischencode ist Basis vieler Programmoptimierungen.
CodegenerierungBearbeiten
Bei der Codegenerierung wird der Programmcode der Zielsprache entweder direkt aus dem Syntaxbaum oder aus dem Zwischencode erzeugt. Falls die Zielsprache eine Maschinensprache ist, kann das Ergebnis direkt ein ausführbares Programm sein oder eine so genannte Objektdatei, die durch das Linken mit der Laufzeitbibliothek und evtl. weiteren Objektdateien zu einer Bibliothek oder einem ausführbaren Programm führt.
GeschichteBearbeiten
Die Geschichte des Compilerbaus wurde von den jeweils aktuellen Programmiersprachen und Hardwarearchitekturen geprägt. Der erste Compiler (A-0) wurde 1952 von der Mathematikerin Grace Hopper entwickelt. Weitere frühe Meilensteine sind 1954 der erste FORTRAN-Compiler und 1960 der erste COBOL-Compiler. Viele Architekturmerkmale heutiger Compiler wurden aber erst in den 1960er Jahren entwickelt.
GfortranBearbeiten
Was ist gfortran?Bearbeiten
gfortran (oder GNU Fortran) ist ein Fortran-Compiler-Frontend für die GNU Compiler Collection (GCC).
InstallationBearbeiten
Vorcompilierte Pakete und Anleitungen zur Installation von gfortran finden sich gegliedert nach Betriebssystem und Prozessortyp auf: [1]
Starten des gfortran-CompilersBearbeiten
MS WindowsBearbeiten
Nach erfolgter Installation befindet sich das gfortran-Softwarepaket beispielsweise im Verzeichnis C:\Programme\gfortran
. Zwecks Funktionstest wird die Eingabeaufforderung von MS Windows gestartet und der gfortran-Compiler aufgerufen:
Das Fortran-Programm für einen ersten konkreten Compilertest könnte so aussehen
program test write (*,*) 'Hallo Welt!' end program test
Dieses Quellprogramm werde via Texteditor für dieses Beispiel unter dem Dateinamen test.f90
im Verzeichnis c:\tmp
gespeichert. Nun wird das Programm compiliert und gebunden
und liegt dann unter dem Programmnamen a.exe
als ausführbares Programm vor. Die Startanweisung für das Programm a.exe
inklusive unmittelbar danach folgender Programmausgabe sieht so aus.
Alternativ kann a.exe
natürlich auch konventionell mittels Windows-Explorer gestartet werden. In diesem speziellen Fall wäre aber bis auf ein kurzes Aufblinken des Eingabeaufforderungs-Fensters nicht viel zu sehen, da das Programm nach der Textausgabe beendet wird.
LinuxBearbeiten
Für die Linux-Variante des gfortran-Compilers gilt prinzipiell dieselbe Vorgehensweise wie für die Windows-Variante.
Compilieren des Beispielprogrammes:
Starten des ausführbaren Programms a.out
und Anzeige der Programmausgabe:
Durch entsprechende Nutzung des Linux-PATH
-Mechanismus (z.B. symbolischer Link in ein PATH-Verzeichnis oder Aufnahme des ./gfortran/bin/
-Verzeichnisses in den PATH
) kann die Angabe des gesamten Compilerpfades bei jedem gfortran
-Compileraufruf entfallen.
Dateiendungen für QuelldateienBearbeiten
Mit gfortran lassen sich Programme verschiedener Fortran-Sprachstandardversionen kompilieren. Der Fortran-Typ wird üblicherweise durch die Dateiendung der Quelldatei festgelegt.
Dateiendung | Fortran-Version |
---|---|
.f | FORTRAN 77 (fixes Zeilenformat) |
.f90 | Fortran 90 (freies Zeilenformat) |
.f95 | Fortran 95 (freies Zeilenformat) |
.f03 | Fortran 2003 (freies Zeilenformat) |
.F | FORTRAN 77 (fixes Zeilenformat) mit Preprocessing durch cpp |
.F90 | Fortran 90 (freies Zeilenformat) mit Preprocessing durch cpp |
.F95 | Fortran 95 (freies Zeilenformat) mit Preprocessing durch cpp |
.F03 | Fortran 2003 (freies Zeilenformat) mit Preprocessing durch cpp |
AnwendungBearbeiten
In der Anwendung gleicht gfortran anderen GCC-Frontends (z. B. gcc, g++ oder g77).
- Übersetzung einer Quelldatei in die ausführbare Datei a.out (bzw. a.exe auf MS Windows-Systemen):
gfortran bsp.f90
- Übersetzung einer Quelldatei in eine Objektdatei bsp.o:
gfortran -c bsp.f90
- Übersetzung einer Quelldatei in die ausführbare Datei bsp:
gfortran -o bsp bsp.f90
- Statisches Linken:
gfortran -static -o bsp bsp.f90
- Mehrere Quelldateien kompilieren und zu einer ausführbaren Datei linken:
gfortran -c bsp1.f90
gfortran -c bsp2.f90
gfortran -o bsp bsp1.o bsp2.o
- Mehrere Quelldateien in einer Anweisung kompilieren und zu einer ausführbaren Datei linken:
gfortran -o bsp bsp1.f90 bsp2.f90
Prüfung des Quellcodes auf StandardkonformitätBearbeiten
Zur Sicherstellung einer strikten Standardkonformität kann die Option std mit folgenden Parametern verwendet werden:
Parameter | Kommentar |
---|---|
f95 | Fortran 95 |
f2003 | Fortran 2003 (noch nicht vollständig implementiert [Stand: Dez. 2005]) |
gnu | Fortran mit GNU-Erweiterungen |
legacy |
- Beispiele:
gfortran -std=f95 bsp.f
gfortran -std=f95 -W -Wall -pedantic bsp.f
HistorischesBearbeiten
Mit g95 [2] stand dem Fortran-Programmierer ein weiterer freier (im Sinne von Freier Software) Kommandozeilen-Compiler zur Verfügung, der für eine Vielzahl von Plattformen erhältlich war. g95 und gfortran basierten auf dem selben Programmcode. Im Jahr 2003 gabelten sich die Entwicklungszweige. gfortran ist nun Teil der GCC. Die Entwicklung von g95 wurde vom ursprünglichen Programmautor Andrew Vaught unabhängig davon weitergeführt. Laut [3] wird g95 seit dem Jahr 2013 nicht mehr weiterentwickelt.
gfortran-WeblinksBearbeiten
Fortran: IfortBearbeiten
AllgemeinesBearbeiten
Der Intel Fortran Compiler for Linux ist ein Fortran-Compiler, der für nicht-kommerzielle Zwecke auch in einer kostenfreien Variante verfügbar ist. Beeindruckend ist auch die diesem Softwarepaket beigegebene ausführliche Dokumentation, vor allem die knapp 900-seitige "Intel Fortran Language Reference". Im Intel Fortran Compiler sind neben den üblichen Fortran-Standardfunktionen auch eine Unmenge eigener Subroutinen und Funktionen implementiert.
InstallationBearbeiten
- Eine Downloadadresse und der Lizenzschlüssel für den Intel Fortran Compilers for Linux werden nach einer Registrierungsprozedur auf der Intel-Website per E-Mail übermittelt.
- Download der Compiler-Software.
- Entpacken der gepackten Datei (gunzip, tar).
- Die eigentliche Installation des Intel Fortran Compilers erfolgt mittels install-Skript.
Dateiendungen für QuelldateienBearbeiten
Mit dem Intel Fortran Compiler lassen sich Programme verschiedener Fortran-Sprachstandardversionen kompilieren. Der Fortran-Typ wird üblicherweise durch die Dateiendung der Quelldatei festgelegt.
Dateiendung | Fortran-Version |
---|---|
.f, .for, .fnt, .i | FORTRAN 77 (fixes Zeilenformat) |
.f90, .i90 | Fortran 90/95 (freies Zeilenformat) |
.F, .FOR, .FTN, .FPP, .fpp | FORTRAN 77 (fixes Zeilenformat) mit Preprocessing |
.F90 | Fortran 90/95 (freies Zeilenformat) mit Preprocessing |
AnwendungBearbeiten
In der Anwendung gleicht der Intel Fortran Compiler dem GNU Fortran Compiler. Die offensichtlichsten Unterschiede sind:
- Die Intel Fortran Compiler-Software wird mittels ifort gestartet
- Die Intel Fortran Compiler-Software kennt die Dateiendungen .f95 und .F95 nicht
- Die Intel Fortran Compiler-Software enthält einen eigenen Kommandozeilendebugger idb.
- idb bietet auch ein GUI .
- Die Benutzung von anderen Debuggern wie gdb ist auch möglich.
- Übersetzung einer Quelldatei in die ausführbare Datei a.out:
- ifort bsp.f90
- Übersetzung einer Quelldatei in eine Objektdatei bsp.o:
- ifort -c bsp.f90
- Übersetzung einer Quelldatei in die ausführbare Datei bsp:
- ifort -o bsp bsp.f90
- Mehrere Quelldateien kompilieren und zu einer ausführbaren Datei linken:
- ifort -c bsp1.f90
- ifort -c bsp2.f90
- ifort -o bsp bsp1.o bsp2.o
- Mehrere Quelldateien in einer Anweisung kompilieren und zu einer ausführbaren Datei linken:
- ifort -o bsp bsp1.f90 bsp2.f90
WeblinksBearbeiten
Was ist eine Programmiersprache?Bearbeiten
In Kurzform: Eine Programmiersprache ist eine Sprache zwecks Abfassung von Computerprogrammen.
Einordnung von FortranBearbeiten
GenerellBearbeiten
- Fortran ist eine höhere Programmiersprache (Higher Level Language, HLL, Programmiersprache der 3. Generation)
- Fortran ist eine prozedurale Programmiersprache (Prozedurale Programmierung)
- Fortran ist eine imperative Programmiersprache (Imperative_Programmierung)
- Fortran ist eine objektorientierte Programmiersprache (ab Fortran 2003, Objektorientierte Programmierung).
- Fortran verwendet das Konzept der starken Typisierung. Fortran kennt explizite und implizite Typisierung (Typisierung (Informatik))
- Fortran ist eine sehr alte Programmiersprache, die aber laufend weiterentwickelt und somit den modernen Trends immer wieder angepasst wurde und wird.
PopularitätBearbeiten
Die Popularität einer Programmiersprache einigermaßen fundiert zu bestimmen ist nicht einfach. Dennoch gibt es Institutionen, die das versuchen, sei es über die Anzahl von Einträgen in Suchmaschinen, Zugriffstatistiken für Internetseiten, Nutzerbefragungen oder auch zeitliche Veränderungen bei Buchverkäufen. Hier wird stellvertretend eine dieser Statistiken angeführt:
Lt. TIOBE lag Fortran im Juli 2021 hinsichtlich Popularität mit einem Rating von 1,12% an 14. Stelle von insgesamt 100 gelisteten Programmiersprachen (mit einer stark steigenden Tendenz im letzten Jahr). Das ist zwar weit hinter dem führenden Dreigespann C, Java und Python. Dennoch rangierte Fortran in dieser Statistik vor anderen bekannten Programmiersprachen, wie z.B. Ruby, Perl, Lisp oder Haskell. Im Mai 2022 rutschte Fortran auf die 30. Stelle ab und erklomm im August 2022 wieder die 19. Stelle. (TPCI - TIOBE Programming Community Index).
Solche Statistiken sind natürlich mit Vorsicht zu geniessen, aber in Form eines groben Richtwerts "Daumen mal Pi" können sie schon einen ersten Eindruck von Verbreitung und Popularität einer Programmiersprache geben.
Im Bereich der numerische Datenverarbeitung, insbesondere auf Hochleistungsrechnern, ist Fortran gemeinsam mit C/C++ nach wie vor führend.
Ein Überblick über die historische Entwicklung von FortranBearbeiten
Jahr | Version | Anmerkungen |
---|---|---|
1954 bis 1957 | FORTRAN, FORTRAN I | entwickelt von einem IBM-Team unter Leitung von John W. Backus, war dies die erste wirklich erfolgreiche höhere Programmiersprache |
1957/58 | FORTRAN II | Inline-Assembler, Kommentare, u.a. |
1958 | FORTRAN III | einige kleinere Änderungen, wurde aber nie offiziell freigegeben |
1961/62 | FORTRAN IV | eine verbesserte und erweiterte Version |
1966 | FORTRAN 66 | die erste standardisierte Fortran-Version und gleichzeitig überhaupt die erste standardisierte höhere Programmiersprache |
1978 | FORTRAN 77 | DO-Schleife, IF THEN-ELSE IF-ELSE-END IF, CHARACTER-Datentyp, u.a. |
1991 | Fortran 90 | free form style, Module, Zeiger, Datenverbund, u.v.m. |
1997 | Fortran 95 | kleinere Änderungen |
2003 | Fortran 2003 | OOP, C-Binding, u.a. |
2010 | Fortran 2008 | Co-Arrays, Submodules, diverse neue Funktionen und viele andere kleinere Änderungen |
2018 | Fortran 2018 | viele kleinere Änderungen |
2023 | Fortran 2023 |
Siehe auch:
- Fortran 2023
- Programmiersprache Fortran 2018 veröffentlicht
- The historical development of Fortran
- A Brief History of FORTRAN/Fortran
- FORTRAN I
ProgrammaufbauBearbeiten
Beispiel: Hallo WeltBearbeiten
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM HALLO C C Das typische Hallo Welt-Programm C WRITE (*,*) 'Hallo Welt!' END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Wie Sie an diesem Beispiel ersehen, weist FORTRAN-77-Code eine charakteristische Struktur auf. Etwas seltsam mutet an, dass die Programmanweisungen nicht am Zeilenanfang stehen, sondern mehrere Leerzeichen eingerückt sind. Das ist obligatorisch, ansonsten würde sich das Programm nicht kompilieren lassen. Die Nummerierungen oberhalb und unterhalb der Programmanweisungen (12345678901...) gehören übrigens nicht zum FORTRAN-Code, sondern sollen nur die Spaltenstruktur von FORTRAN-77-Programmen verdeutlichen und die Orientierung etwas erleichtern.
Weiter ist zu erkennen, dass das Beispiel sehr kurz und aussagekräftig ausfällt. Die Anweisung in der ersten Zeile kennzeichnet die Programmeinheit als Hauptprogramm und gibt ihr die Bezeichnung HALLO
. Die nächsten drei Zeilen sind Kommentarzeilen, erkennbar am Buchstaben in der ersten Spalte der Zeile. Dann folgt die Anweisung, einen String auf die Standardausgabe zu schreiben. Und schließlich signalisiert die END
-Anweisung das Programmende.
Das ZeilenformatBearbeiten
Normalerweise gilt, dass jede FORTRAN-Anweisung in einer eigenen Zeile steht. Eine Spezialität von FORTRAN 77 ist die Spaltenorganisation eines Programmes: das fixe Zeilenformat. Es gibt fixe Spalten (Zeichenpositionen) in denen bestimmte Inhalte stehen müssen bzw. dürfen. Diese Art der Codeanordnung rührt von den Anforderungen der Lochkarte her. Bei den damaligen Großrechenanlagen war die Eingabe von Programmen häufig nur in Form von Lochkartenstapeln möglich. FORTRAN 77 nimmt auf diese Beschränkung Rücksicht.
Der generelle Aufbau des fixen Zeilenformates von FORTRAN 77 ist wie folgt:
In der nachstehenden Tabelle wird die Bedeutung der einzelnen Spalten detaillierter dargestellt.
Spalte | Inhalt | Bedeutung |
---|---|---|
1 | C oder * | Kennzeichnet eine Kommentarzeile |
1 bis 5 | Eine Zahl 1 bis 99999 | Anweisungsnummer (Marke) |
6 | Leerzeichen oder 0 (Null) | Beginn einer Anweisung (das ist der Normalfall) |
6 | sonstiges Zeichen | Fortsetzungszeile (standardmäßig sind bis zu 19 Fortsetzungszeilen erlaubt) |
7 bis 72 | FORTRAN-Befehl (Anweisung) | |
73 bis 80 | beliebige Zeichen | Kommentar (ursprünglich für Lochkarten-Sequenznummern) |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP * Leerzeilen werden übrigens wie Kommentarzeilen behandelt * Eine 0 (Null) an der sechsten Position entspricht einem Leerzeichen, * foerdert aber nicht gerade die Uebersichtlichkeit 0A = 5 B = 7 C = A + * Und jetzt kommt eine Fortsetzungszeile $B WRITE (*,*) C END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Besondere Vorsicht ist bei langen Anweisungen geboten. Alles nach der 72 Zeichenposition wird nicht mehr als Teil der Anweisung aufgefasst. Im günstigsten Fall wirft der Compiler in einem solchen Fall bei der Übersetzung einen Syntaxfehler aus.
Die Programmstruktur für das HauptprogrammBearbeiten
Ein Hauptprogramm weist immer folgende Struktur auf
PROGRAM
prname
- Vereinbarungsteil
- Aktionsteil
END
prname
ist ein symbolischer Name für das Hauptprogramm und kann mehr oder minder willkürlich festgelegt werden. Das erste Zeichen muss immer ein Buchstabe sein. Im Vereinbarungsteil werden z. B. die Variablen deklariert. Im Aktionsteil wird dann der eigentliche Programmablauf festgelegt. END
kennzeichnet das Programmende. Theoretisch könnte im Hauptprogramm die erste Zeile (PROGRAM prname
) auch komplett entfallen. In älteren Programmcodes wurde das durchaus auch so gehandhabt. Allerdings leidet darunter die Übersichtlichkeit des Programmes. Die END
-Anweisung muss auf jeden Fall angegeben werden.
Der FORTRAN-ZeichenvorratBearbeiten
FORTRAN-77-Programme bestehen standardmäßig nur aus folgenden Zeichen
- Großbuchstaben: A bis Z
- Ziffern: 0 bis 9
- 13 Sonderzeichen: + - * / = ( ) : , . ' $ und dem Leerzeichen
Viele FORTRAN-77-Compiler akzeptieren auch Kleinbuchstaben. Zeichenkettenliterale können natürlich alle ASCII-Zeichen beinhalten.
Symbolische NamenBearbeiten
Standardmäßig dürfen symbolische Namen maximal sechs Zeichen lang sein. Als erstes Zeichen muss immer ein Buchstabe (A-Z) stehen, der Rest muss alphanumerisch sein (Buchstabe oder Ziffer). „Lustigerweise“ dürfen bei FORTRAN 77 Leerzeichen auch innerhalb eines symbolischen Namens auftreten.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PR O GRA M BSP * * gfortran, g95, ifort etc. kompilieren diesen Code und * bei der Programmausfuehrung wird auch das richtige * Ergebnis angezeigt. * ALPHA = 5 BETA = 7 GAMm a = A L PH A + B E TA WRI TE (*,*) G A MM A EN D |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Besonders unangenehm kann dieses Verhalten werden, wenn in einer Zählschleife anstelle eines Kommas irrtümlicherweise ein Punkt gesetzt wird, wenn also z. B. anstelle
DO 10 I = 1, 3
fälschlicherweise
DO 10 I = 1. 3
steht. Letzteres entspricht nämlich der Zuweisung der Zahl 1.3
an die Variable DO10I
.
Gleiches gilt, wenn ähnlich wie in den Programmiersprachen C, C++ oder Java versucht wird, einer Variablen gleich in einer Deklarationszeile einen Wert zuzuweisen, z. B.
REAL A = 10.5 WRITE(*,*) A
Hier wird irgendein Wert ausgegeben, aber mit an Sicherheit grenzender Wahrscheinlichkeit nicht 10.500000, denn die Wertzuweisung erfolgte an die Variable REALA
und nicht an A
.
Datentypen, Variablen, Wertzuweisungen, KonstantenBearbeiten
Dieses Kapitel handelt von Datentypen, Variablen, Konstanten und der Wertzuweisung.
DatentypenBearbeiten
Arithmetische DatentypenBearbeiten
FORTRAN 77 unterscheidet standardmäßig zwischen vier arithmetischen Datentypen:
Datentyp | Kommentar | Literale (Beispiele) |
---|---|---|
INTEGER |
Ganzzahlen | 15 , -6500 , 200 000 000
|
REAL |
Reelle Zahlen einfacher Genauigkeit | 3.1415 , -5.5 , .7E3 , 12.5E-5
|
DOUBLE PRECISION |
Reelle Zahlen doppelter Genauigkeit | 3.1415D0 , -5.5D0 , .7D3 , 12.5D-5
|
COMPLEX |
Komplexe Zahlen (zwei REAL-Zahlen) | (3.1415, -5.5) , (1.4, 7.1E4)
|
Logischer DatentypBearbeiten
Datentyp | Kommentar | Literale (alle) |
---|---|---|
LOGICAL |
Logischer Datentyp (wahr oder falsch) | .TRUE. , .FALSE.
|
Manchmal sind in alten FORTRAN-Programmen auch folgende Schreibweisen zu finden, welche jedoch nicht standardkonform sind:
INTEGER*4
,REAL*4
,LOGICAL*4
(Standardgrößen)INTEGER*1
,LOGICAL*1
INTEGER*2
,LOGICAL*2
REAL*8
(entspricht DOUBLE PRECISION)COMPLEX*16
(komplexe Zahlen mit zwei DOUBLE-PRECISION-Elementen)
Die Zahlen geben den Speicherplatzbedarf in Byte an.
ZeichenkettenBearbeiten
Datentyp | Kommentar | Beispiel (Konstante) |
---|---|---|
CHARACTER *n |
Zeichenkette (String) mit einer Länge von n Zeichen | 'Hallo, Welt!' |
CHARACTER |
Zeichenkette mit einer Länge von einem Zeichen (entspricht CHARACTER*1 ) |
'A' |
Beachte: Im Gegensatz zu vielen anderen Programmiersprachen werden Zeichenketten in FORTRAN 77 nicht in Anführungszeichen eingeschlossen, sondern in Apostrophe.
Tritt in einem String ein Apostroph auf, so muss dieses verdoppelt werden, z. B.
'Wie geht''s?'
Beispiel:
CHARACTER*5 STR STR = 'Hallo'
Alternative Schreibweise:
CHARACTER STR*5 STR = 'Hallo'
VariablenBearbeiten
Eine Variable ist charakterisiert durch einen
- symbolischen Namen
- Datentyp
- Wert
- Speicherplatz
Beim Programmstart hat eine Variable keinen definierten Wert. Eine Variable kann ihren Datentyp auf zwei Arten erhalten, durch implizite oder explizite Typanweisung.
Implizite TypanweisungBearbeiten
Bei der impliziten Typanweisung bestimmt der Anfangsbuchstabe des Variablenbezeichners den Datentyp.
Anfangsbuchstabe der Variablen | Impliziter Datentyp |
---|---|
I, J, K, L, M oder N | INTEGER |
alle restliche Buchstaben | REAL |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP B1 = 8.9 C1 = 3. I1 = B1/C1 WRITE (*,*) I1 C Das Ergebnis ist 2, da I1 implizit als INTEGER definiert ist END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Die Standardzuordnung der Anfangsbuchstaben kann durch das Schlüsselwort IMPLICIT
auch geändert werden.
Es gibt noch ein weiteres Problem dieser impliziten Datentypzuordnung. Durch Tippfehler können unbeabsichtigt neue Variablen entstehen. Entschärft werden kann diese Tatsache durch folgende Festlegung im Vereinbarungsteil des Programms:
IMPLICIT LOGICAL (A-Z)
Dadurch werden alle Variablen mit implizit festgelegtem Datentyp automatisch zu Variablen mit logischem Datentyp. In vielen Fällen konnten so Tippfehler bei Variablennamen schnell eingegrenzt werden.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP IMPLICIT LOGICAL (A-Z) REAL REE REE = 5.8 C Tippfehler: REA anstatt REE WRITE (*,*) REA + 2.1 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Der Compilierungsversuch mit g77, gfortran und g95 endet mit einer Fehlermeldung. Bei gfortran sieht das so aus
In file test.f:9 WRITE (*,*) REA + 2.1 1 Error: Operands of binary numeric operator '+' at (1) are LOGICAL(4)/REAL(4)
Doch der Intel Fortran Compiler 9.0 zeigt die Grenzen der Sinnhaftigkeit der IMPLICIT
-Anweisung in der dargestellten Art und Weise beim heutigen Einsatz von FORTRAN-77-Code auf. Dieser Compiler akzeptiert den Beispielcode warnhinweislos und liefert bei Programmausführung den Wert 2.1
. Wirklich Abhilfe schafft also erst die IMPLICIT NONE
-Anweisung. Diese legt eindeutig fest, dass ausschließlich die explizite Datentypfestlegung Verwendung finden soll. Allerdings ist IMPLICIT NONE
erst ab Fortran-90/95-Standard. In einigen noch erhältlichen FORTRAN-77-Compilern, wie dem g77, ist diese Anweisung im Vorgriff auf den genannten neueren Standard bereits implementiert.
Explizite TypanweisungBearbeiten
Durch die Vorgabe von
datentyp variablenbezeichner |
im Vereinbarungsteil des Programms wird der Datentyp einer Variablen explizit festgelegt. Die explizite Typanweisung hat gegenüber der impliziten Typanweisung Vorrang.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP IMPLICIT LOGICAL (A-Z) REAL B REAL C REAL I B = 8.9 C = 3. I = B/C WRITE (*,*) I C Das Ergebnis ist 2.966666 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Benannte KonstantenBearbeiten
Benannte Konstanten können durch das PARAMETER
-Schlüsselwort festgelegt werden.
CHARACTER*5 STR PARAMETER (PI=3.1415, PIFAK=PI/2., STR='Hallo')
Der zugewiesene Wert kann eine Konstante (Literal) oder eine schon definierte benannte Konstante sein. Der Datentyp muss vorher vereinbart werden oder ist implizit bekannt.
Für Zeichenketten ist im Zusammenwirken mit PARAMETER
auch eine *-Schreibweise möglich. Dies erspart die explizite Angabe der Stringlänge.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*(*) A PARAMETER (A = 'Jetzt wird auch die Stringlaenge festgelegt') WRITE (*,*) A C Ausgabe: Jetzt wird auch die Stringlaenge festgelegt END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
WertzuweisungBearbeiten
Wertzuweisungen haben wir schon kennengelernt:
variable = ausdruck |
Beispiel:
K = 1 K = K + 2
Die Wertzuweisung an eine Variable ist, wie am vorigen Beispiel und auch nachfolgend zu ersehen, nicht zu verwechseln mit einer mathematischen Gleichung. Der Ausdruck
K + 2 = 5
wäre zwar mathematisch korrekt. Als Wertzuweisung in einem FORTRAN-Programm ist dies aber keine gültige Formulierung. K + 2
ist kein zulässiger Ausdruck auf der linken Seite des Zuweisungsoperators (L-Wert).
Beachte: In FORTRAN 77 ist auch keine Kette von Wertzuweisungen möglich. Der folgende Ausdruck ist in FORTRAN 77 nicht erlaubt und liefert eine Fehlermeldung.
I = J = K = 1.5 C Fehler!
FelderBearbeiten
Bei allem, was mehr oder weniger wie ein Vektor, eine Matrix oder eine sonstige Aneinanderreihung von gleichartigen Elementen aussieht, kann der Einsatz von Feldern (Arrays) sinnvoll sein.
Eindimensionale FelderBearbeiten
Für die Deklaration von eindimensionalen Feldern gibt es mehrere Möglichkeiten. Die Feldgrenzen müssen konstante Werte sein. Die Varianten werden nun anhand von Beispielen gezeigt.
Variante 1: EinfachBearbeiten
REAL ARR(10)
Beachte: Der Feldindex läuft hier von 1 bis 10 und nicht von 0 bis 9, wie es bei vielen modernen Hochsprachen der Fall ist.
Variante 2: Das DIMENSION
-SchlüsselwortBearbeiten
REAL ARR DIMENSION ARR(10)
Variante 3: Verwendung von benannten KonstantenBearbeiten
INTEGER MAXIND PARAMETER (MAXIND=10) REAL ARR(MAXIND)
Hier erfolgt die Festlegung der Feldgröße über eine benannte Konstante.
Variante 4: Explizite Angabe der IndexgrenzenBearbeiten
REAL ARR(0:9)
Hier wird Unter- und Obergrenze explizit angegeben. Der Index läuft nun von 0 bis 9. Auch negative Werte für die Indizes sind möglich, z. B.
REAL ARR(-4:5)
BeispielBearbeiten
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER MAXIND PARAMETER (MAXIND=10) REAL ARR(MAXIND) C ACHTUNG! Array startet mit dem Index 1 C ARR(0) waere ein Fehler! ARR(1) = 1.5 ARR(2) = 2.5 ARR(10) = 10.5 WRITE (*,*) ARR(1) C 1.5 wird ausgegeben WRITE (*,*) ARR(10) C 10.5 wird ausgegeben END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Ein
WRITE (*,*) ARR
listet den gesamten Feldinhalt.
1.500000 2.500000 0.000000 3.9876625E-34 0.000000 3.9902670E-34 -2.7682916E-05 -2.7269103E-05 -2.9040850E-05 10.50000
Im Beispielsfall wurden die Feldelemente ARR(3)
bis ARR(9)
nicht explizit vorbelegt. Sie sind deshalb undefinierten Inhalts und können bei jedem Programmaufruf andere Werte annehmen.
Mehrdimensionale FelderBearbeiten
Für mehrdimensionale Felder gelten die gleichen Varianten wie für eindimensionale Felder. Standardmäßig kann ein Feld bis zu sieben Dimensionen besitzen. Die Speicherreihenfolge ist spaltenorientiert. Das bedeutet, der erste Index variiert am schnellsten:
Beispiel: Ein 2-dimensionales FeldBearbeiten
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*10 ARR(0:9, 2:5) ARR(0, 2) = 'Hallo' ARR(1, 2) = 'Welt' C ... ARR(9, 5) = 'Universum' WRITE (*,*) ARR(0, 2) C Hallo WRITE (*,*) ARR(9, 5) C Universum END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Beispiel: Spaltenorientierte SpeicherreihenfolgeBearbeiten
Die 3×3-Matrix soll in ein Fortran-Programm eingelesen und wieder komplett ausgegeben werden. Zusätzlich soll auch der Wert des Feldelementes a23 (2. Zeile, 3.Spalte, Wert = -2) separat ausgegeben werden.
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER ARR(3,3) C Feldelemente einlesen WRITE (*,*) 'Werte (spaltenorientierte Eingabe):' READ (*,*) ARR C Komplettes Feld ausgeben WRITE (*,*) 'Gesamtfeld = ' , ARR C a23 ausgeben WRITE (*,*) 'a23 = ', ARR(2,3) END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Ein-/Ausgabe:
Werte (spaltenorientierte Eingabe): 1 40 -1 -5 3 9 0 -2 65 Gesamtfeld = 1 40 -1 -5 3 9 0 -2 65 a23 = -2
Arithmetische AusdrückeBearbeiten
Arithmetische OperatorenBearbeiten
FORTRAN 77 kennt folgende arithmetische Operatoren
Operator | Kommentar | Mathematische Entsprechung |
---|---|---|
A + B |
Addition | |
A - B |
Subtraktion | |
A * B |
Multiplikation | |
A / B |
Division | |
A ** B |
Exponentiation |
Mit dem Exponentiationsoperator (Potenzierung) war und ist FORTRAN 77 anderen Programmiersprachen einen Schritt voraus. Andererseits kennt FORTRAN 77 den aus vielen anderen Programmiersprachen bekannten Modulo-Operator nicht. Als Überkompensation gibt es für diesen Zweck die MOD()
-Funktion sowohl für Ganzzahlen, wie auch für Fließkommazahlen.
OperatorenprioritätBearbeiten
Die Prioriät der arithmetischen Operatoren entspricht den mathematischen Gesetzmäßigkeiten.
- Klammerung vor allem anderen, z. B.
(A+B)*C
A*C+B*C
- Exponentiation vor Punktrechnung, z. B.
A*B**C
A*(B**C)
- Punktrechnung vor Strichrechnung, z. B.
A+B*C
A+(B*C)
Berechnungsfolge bei gleicher PrioritätBearbeiten
- Klammerung, Punktrechnung und Strichrechnung:
Beispiel:A*B/C*D
((A*B)/C)*D
- Exponentiation:
Beispiel:A**B**C
A**(B**C)
Außerdem ist zu beachten, dass niemals zwei Operatoren direkt aufeinander folgen dürfen.
Beispiel: Der Ausdruck 1.5**-1
ist in FORTRAN 77 falsch und führt zu einer Fehlermeldung. Richtig ist 1.5**(-1)
ErgebnisdatentypBearbeiten
Operanden gleichen DatentypsBearbeiten
Bei Operanden gleichen Datentyps erhält das Ergebnis den Datentyp der Operanden.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP REAL A A = 3/2 C 3 ist ein INTEGER und 2 ist auch ein INTEGER, C daher muss das Ergebnis auch ein INTEGER sein, also 1. C Die Zuweisung an die REAL-Variable A stellt das C Ergebnis nicht mehr richtig. WRITE (*,*) A C Ausgabe: 1.00000 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Implizite Typumwandlung bei Operanden gemischten DatentypsBearbeiten
Weisen die Operanden unterschiedliche Datentypen auf, so wird bei jeder Operation, falls nötig, das Ergebnis dem höherwertigen Datentyp angepasst.
INTEGER REAL DOUBLE PRECISION COMPLEX |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP REAL A A = 3/2. C 2. ist ein REAL. Jetzt stimmt das Ergebnis. WRITE (*,*) A C Ausgabe: 1.500000 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Explizite TypumwandlungBearbeiten
FORTRAN 77 besitzt auch Funktionen zur expliziten Umwandlung des Datentyps. Diese werden im Kapitel Standardfunktionen näher beschrieben.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP REAL R COMPLEX C R = 2 C = CMPLX(R) WRITE (*,*) C C Ausgabe: ( 2.000000 , 0.000000 ) END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Logische AusdrückeBearbeiten
Logische Ausdrücke können zwei Zustände annehmen, wahr oder falsch. Diese werden in FORTRAN 77 durch die Literale .TRUE.
oder .FALSE.
dargestellt.
Logische OperatorenBearbeiten
Folgende Tabelle enthält alle in FORTRAN 77 bekannte logische Operatoren. Sie sind in der Reihenfolge ihrer Prioritäten absteigend geordnet.
Operator | Kommentar | Mathematische Entsprechung |
---|---|---|
.NOT. A |
logisches NICHT | |
A .AND. B |
logisches UND | |
A .OR. B |
logisches ODER | |
A .EQV. B |
logische Äquivalenz (XNOR) | |
A .NEQV. B |
logische Antivalenz (XOR) |
WahrheitstafelBearbeiten
A | B | .NOT. A | A .AND. B | A .OR. B | A .EQV. B | A .NEQV. B |
---|---|---|---|---|---|---|
.TRUE. | .TRUE. | .FALSE. | .TRUE. | .TRUE. | .TRUE. | .FALSE. |
.TRUE. | .FALSE. | .FALSE. | .FALSE. | .TRUE. | .FALSE. | .TRUE. |
.FALSE. | .TRUE. | .TRUE. | .FALSE. | .TRUE. | .FALSE. | .TRUE. |
.FALSE. | .FALSE. | .TRUE. | .FALSE. | .FALSE. | .TRUE. | .FALSE. |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP LOGICAL L L = .TRUE. WRITE (*,*) .NOT. L C Ausgabe: F END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP LOGICAL A, B A = .TRUE. B = .FALSE. WRITE (*,*) A .NEQV. B C Ausgabe: T END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
VergleichsausdrückeBearbeiten
Zum Vergleichen zweier arithmetischer Ausdrücke oder von Strings gibt es in FORTRAN 77 Vergleichsoperatoren. Das Ergebnis eines Vergleichs ist immer logischer Wert (.TRUE.
oder .FALSE.
).
Vergleichsoperatoren für arithmetische TypenBearbeiten
Operator | Kommentar | Mathematische Entsprechung |
---|---|---|
A .LT. B |
less than (kleiner als) | |
A .LE. B |
less equal (kleiner gleich) | |
A .GT. B |
greater than (größer als) | |
A .GE. B |
greater equal (größer gleich) | |
A .EQ. B |
equal (gleich) | |
A .NE. B |
not equal (ungleich) |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER A, B A = 5 B = 6 WRITE (*,*) A .LT. B C Ausgabe: T END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*5 A, B A = 'Halli' B = 'Hallo' WRITE (*,*) A .LT. B C Ausgabe: T END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Beim Rechnen mit Fließkommazahlen (Datentypen: REAL
, DOUBLE PRECISION
, COMPLEX
) sind die systemimmanenten Rechenungenauigkeiten zu beachten. Aus diesem Grund sollten Fließkommazahlen nicht auf strikte (Un)Gleichheit geprüft werden, sondern Vergleiche sollten einen kleinen Toleranzbereich aufweisen: .
Beispiel (hier mit und :
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
X = LOG(A) C Statt... IF (X .EQ. 2) C besser IF (ABS(X - 2) .LT. .00001) |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
ZeichenkettenvergleicheBearbeiten
Das Ergebnis eines Vergleichs von Zeichenketten mittels Vergleichsoperatoren ist teilweise systemabhängig. Ausnahmen sind .EQ.
und .NE.
. Systemunabhängige Resultate sind durch Verwendung der entsprechenden lexikalischen Standardfunktionen erhältlich. Dort wird immer die Reihenfolge im ASCII-Zeichensatz verwendet.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP IMPLICIT LOGICAL(A-Z) CHARACTER*15 A, B A = 'Hallö' B = 'hallo' WRITE (*,*) 'A gleich B? ', A .EQ. B WRITE (*,*) 'A kleiner als B (Operator)? ', A .LT. B WRITE (*,*) 'A kleiner als B (Funktion)? ', LLT (A, B) C Ausgabe: C A gleich B? F C A kleiner als B (Operator)? T C A kleiner als B (Funktion)? T END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
OperatorenprioritätBearbeiten
- Klammerung
- Arithmetische Operatoren
- Vergleichsoperatoren
- Logische Operatoren
- Zuweisungsoperator
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP IMPLICIT LOGICAL(A-Z) REAL A, B, C LOGICAL X, RES A = 5.5 B = -1.2 C = 8.6 X = .FALSE. RES = X .AND. A - B .GT. C .OR. A .LE. C C entspricht infolge Op.priorität: C RES = ((X .AND. ((A-B) .GT. C)) .OR. (A .LE. C)) WRITE (*,*) RES C Ausgabe: T END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Vergleichsoperatoren und logische Operatoren finden in erster Linie bei Verzweigungen und Schleifenbedingungen Verwendung.
StringoperationenBearbeiten
FORTRAN 77 bietet vergleichsweise komfortable Operatoren zur Behandlung von Zeichenketten.
VerknüpfungsoperatorBearbeiten
Operator | Kommentar |
---|---|
A // B |
Operator zum Verknüpfen von Zeichenketten |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*4 A, B*10 A='How ' B='do you do.' WRITE (*,*) A // B C Ausgabe: How do you do. END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
TeilkettenBearbeiten
Ein String ist ein CHARACTER
-Feld. Auch Teilketten einer solchen Zeichenkette können adressiert werden.
Prinzip | Beschreibung |
---|---|
stringname (anfang:ende) | von anfang bis ende |
stringname (:ende) | vom ersten Zeichen bis ende |
stringname (anfang:) | von anfang bis zum letzten Zeichen |
stringname (index:index) | genau ein Zeichen an der Position index |
Dabei muss anfang stets größer oder gleich Eins sein. ende darf nicht größer als die Länge der Zeichenkette sein. index muss sich stets zwischen Eins und der Länge der Zeichenkette befinden.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*10 A A='Hallo Welt' WRITE (*,*) A(2:4) C Ausgabe: all WRITE (*,*) A(5:) C Ausgabe: o Welt WRITE (*,*) A(:3) C Ausgabe: Hal END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*10 A A='Hallo Welt' A(7:) = 'XYZ' WRITE (*,*) A C Ausgabe: Hallo XYZ END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Neben diesen Möglichkeiten sind in FORTRAN 77 auch einige Standardfunktionen für das Hantieren mit Zeichenketten vorgesehen. Diese sind im Kapitel Standardfunktionen beschrieben.
Verzweigungen und SchleifenBearbeiten
GOTOBearbeiten
GOTO
bewirkt einen Sprung zu einer bestimmten Anweisungsnummer.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP GOTO 100 WRITE (*,*) 'Hallo' 100 WRITE (*,*) 'Welt' C Ausgabe: Welt END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
CONTINUEBearbeiten
CONTINUE
ermöglicht bei Anweisungsnummern eine „leere“ Anweisung.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP GOTO 100 WRITE (*,*) 'Hallo' 100 CONTINUE C keine Ausgabe END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Bedingtes GOTOBearbeiten
Beim bedingten GOTO
ist in Abhängigkeit von einer Integer-Variablen der Sprung zu einer bestimmten Anweisungsnummer möglich.
Beispiel: Eine „Switch“-Verzweigung
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I I=2 GOTO (100, 200, 300), I 100 WRITE (*,*) 'Hallo 1' GOTO 1000 200 WRITE (*,*) 'Hallo 2' GOTO 1000 300 WRITE (*,*) 'Hallo 3' 1000 CONTINUE C Ausgabe: Hallo 2 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
IF-VerzweigungenBearbeiten
Der IF-EinzeilerBearbeiten
IF (logischer ausdruck) anweisung |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I I=2 IF (I .EQ. 2) WRITE (*,*) 'Hallo' C Ausgabe: Hallo END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
IF-THENBearbeiten
IF (logischer ausdruck) THEN anweisungsblock END IF |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I I=2 IF (I .EQ. 2) THEN WRITE (*,*) 'Hallo' END IF C Ausgabe: Hallo END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
IF-THEN-ELSEBearbeiten
IF (logischer ausdruck) THEN if-anweisungsblock ELSE else-anweisungsblock END IF |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I I=333 IF (I .GE. 444) THEN WRITE (*,*) 'Hallo' ELSE WRITE (*,*) 'Hola' END IF C Ausgabe: Hola END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
ELSE-IFBearbeiten
IF (logischer ausdruck 1) THEN if-anweisungsblock 1 ELSE IF (logischerAusdruck 2) THEN if-anweisungsblock 2 ELSE IF (logischerAusdruck n) THEN if-anweisungsblock n ELSE else-anweisungsblock END IF |
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I I = 2 IF (I .EQ. 1) THEN WRITE (*,*) 'I ist eins' ELSE IF (I .EQ. 2) THEN WRITE (*,*) 'I ist zwei' ELSE WRITE (*,*) 'Ich weiß nicht was I ist' END IF C Ausgabe: I ist zwei END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
DO-SchleifenBearbeiten
Die DO
-Schleife (Zählschleife) ist die einzige Schleife die FORTRAN 77 standardmäßig kennt.
DO nr zählvariable = startwert, endwert [, schrittweite] anweisungsblock nr CONTINUE |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I DO 100 I = 1, 10 WRITE (*,*) I 100 CONTINUE C Zeilenweise Ausgabe der Zahlen 1 bis 10 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Weitere SchleifenBearbeiten
Da FORTRAN 77 keine weiteren Schleifen kennt, müssen diese mit Hilfe einer IF-Verzweigung und einem GOTO
-Befehl nachgebildet werden. Prominente Beispiele aus anderen Programmiersprachen sind die While-Schleife und die Do-While-Schleife.
While-Schleife (kopfgesteuerte Schleife)Bearbeiten
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I I=0 10 IF(I .LT. 5) THEN WRITE (*,*) I I = I + 1 GOTO 10 END IF C Die Zahlen 0 bis 4 werden ausgegeben END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Do-While-Schleife (fußgesteuerte Schleife)Bearbeiten
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I I=6 10 CONTINUE WRITE (*,*) I I = I + 1 IF (I .LT. 5) GOTO 10 C Die Zahl 6 wird ausgegeben END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Implizite SchleifeBearbeiten
Bei Eingabe oder Ausgabe ist die Angabe einer impliziten Schleife möglich.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I WRITE (*,*) ('Hallo', I = 1, 10) C Ausgabe: HalloHalloHalloHalloHalloHalloHalloHalloHalloHallo END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
STOPBearbeiten
Die STOP
-Anweisung beendet das Programm.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP WRITE (*,*) 'Vor Stop-Statement' STOP WRITE (*,*) 'Nach Stop-Statement' C Ausgabe: Vor Stop-Statement END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
StandardfunktionenBearbeiten
In diesem Kapitel werden für die Funktionsargumente und Rückgabewerte nachfolgende Kürzel verwendet, die Aufschluss über die möglichen Datentypen geben.
Kürzel | Datentypen |
---|---|
i | INTEGER
|
r | REAL
|
d | DOUBLE PRECISION
|
x | COMPLEX
|
c | CHARACTER*n
|
l | LOGICAL
|
DatentypumwandlungBearbeiten
Umwandlung in INTEGERBearbeiten
- i = INT(i)
Abschneiden des Nachkommaanteils:
- i = INT(r)
- i = IFIX(r)
- i = IDINT(d)
- i = INT(x)
ASCII-Wert des Zeichens c:
- i = ICHAR(c)
Umwandlung in REALBearbeiten
- r = REAL(i)
- r = FLOAT(i)
- r = REAL(r)
- r = SNGL(d)
- r = REAL(x)
Umwandlung in DOUBLEBearbeiten
- d = DBLE(i)
- d = DBLE(r)
- d = DBLE(d)
- d = DBLE(x)
Umwandlung in COMPLEXBearbeiten
- x = CMPLX(i)
- x = CMPLX(r)
- x = CMPLX(d)
- x = CMPLX(x)
Umwandlung in CHARACTERBearbeiten
Gibt das Zeichen zum ASCII-Wert i zurück.
- c = CHAR(i)
Mathematische FunktionenBearbeiten
Abschneiden des NachkommaanteilsBearbeiten
Ist das Argument größer Null, wird die nächstkleinere ganze Zahl zurückgegeben. Ist das Argument kleiner Null, wird die nächstgrößere ganze Zahl zurückgegeben.
- r = AINT(r)
- d = DINT(d)
RundenBearbeiten
Ist das Argument größer oder gleich Null, ist der Rückgabewert INT(X+0.5)
. Ist das Argument kleiner Null, ist der Rückgabewert INT(X-0.5)
- r = ANINT(r)
- d = DNINT(d)
- i = NINT(r)
- i = IDNINT(d)
AbsolutwertBearbeiten
- i = IABS(i)
- r = ABS(r)
- d = DABS(d)
- r = CABS(x)
Double Precision-ProduktBearbeiten
Rückgabewert ist r1 × r2 mit Datentyp DOUBLE PRECISION
- d = DPROD(r1, r2)
ModuloBearbeiten
Rückgabewert ist zahl1 - INT(zahl1 /zahl2)*zahl2
- i = MOD(i1, i2)
- r = AMOD(r1, r2)
- d = DMOD(d1, d2)
VorzeichentransferBearbeiten
Wenn die zahl2 >= 0
ist, dann wird |zahl1|
zurückgegeben.
Wenn die zahl2 < 0
ist, dann wird -|zahl1|
zurückgegeben.
- i = ISIGN(i1, i2)
- r = SIGN(r1, r2)
- d = DSIGN(d1, d2)
Positive DifferenzBearbeiten
Für zahl1 > zahl2
ist der Rückgabewert zahl1 - zahl2
.
Für zahl1 <= zahl2
wird Null zurückgegeben.
- i = IDIM(i1, i2)
- r = DIM(r1, r2)
- d = DDIM(d1, d2)
MaximumBearbeiten
Gibt den größten Argumentwert zurück.
- i = MAX0(i1, i2, ...)
- r = AMAX1(r1, r2, ...)
- d = DMAX1(d1, d2, ...)
- r = AMAX0(i1, i2, ...)
- i = MAX1(r1, r2, ...)
MinimumBearbeiten
Gibt den kleinsten Argumentwert zurück.
- i = MIN0(i1, i2, ...)
- r = AMIN1(r1, r2, ...)
- d = DMIN1(d1, d2, ...)
- r = AMIN0(i1, i2, ...)
- i = MIN1(r1, r2, ...)
Komplexe ZahlenBearbeiten
Gibt den Imaginärteil zurück:
- r = AIMAG(x)
Gibt die konjugiert komplexe Zahl zurück:
- x = CONJG(x)
QuadratwurzelBearbeiten
Gibt die Quadratwurzel zurück:
- r = SQRT(r)
- d = DSQRT(d)
- x = CSQRT(x)
ExponentialfunktionBearbeiten
Gibt natürliche Exponentialfunktion zurück:
- r = EXP(r)
- d = DEXP(d)
- x = CEXP(x)
Logarithmus naturalisBearbeiten
Gibt den natürlichen Logarithmus zurück:
- r = ALOG(r)
- d = DLOG(d)
- x = CLOG(x)
Dekadischer LogarithmusBearbeiten
Gibt den dekadischen Logarithmus zurück:
- r = ALOG10(r)
- d = DLOG10(d)
WinkelfunktionenBearbeiten
- r = SIN(r)
- d = DSIN(d)
- x = CSIN(x)
- r = COS(r)
- d = DCOS(d)
- x = CCOS(x)
- r = TAN(r)
- d = DTAN(d)
ArkusfunktionenBearbeiten
- r = ASIN(r)
- d = DASIN(d)
- r = ACOS(r)
- d = DACOS(d)
- r = ATAN(r)
- d = DATAN(d)
Gibt zurück:
- r = ATAN2(r1, r2)
- d = DATAN2(d1, d2)
Diese Funktionen sind für ähnliche Werte der beiden Argumente erheblich genauer.
HyperbelfunktionenBearbeiten
- r = SINH(r)
- d = DSINH(d)
- r = COSH(r)
- d = DCOSH(d)
- r = TANH(r)
- d = DTANH(d)
Zeichenketten-FunktionenBearbeiten
LängeBearbeiten
- i = LEN(c)
Index eines TeilstringsBearbeiten
Gibt die erste Position des Auftretens eines Teilstrings c2 in c1 zurück. c2 muss eine (benannte) Konstante sein.
- i = INDEX(c1, c2)
Lexikalische FunktionenBearbeiten
Hier wird unabhängig von der Plattform immer der ASCII-Zeichensatz als Grundlage verwendet.
Lexikalisch größer oder gleich (Rückgabewert ist .TRUE.
wenn c1 >= c2):
- l = LGE(c1, c2)
Lexikalisch größer als (Rückgabewert ist .TRUE.
wenn c1 > c2):
- l = LGT(c1, c2)
Lexikalisch kleiner oder gleich (Rückgabewert ist .TRUE.
wenn c1 <= c2):
- l = LLE(c1, c2)
Lexikalisch kleiner als (Rückgabewert ist .TRUE.
wenn c1 < c2):
- l = LLT(c1, c2)
Obige Funktionenauflistung basiert auf dem Fortran 77 Sprachstandard X3J3/90.4, Kap.15: Functions and Subroutines.
UnterprogrammeBearbeiten
Natürlich können in FORTRAN 77 auch eigene Unterprogramme erstellt werden.
FunktionsanweisungBearbeiten
Eine Funktionsanweisung (auch Anweisungsfunktion genannt) stellt die einfachste Möglichkeit dar, ein Unterprogramm in FORTRAN 77 zu realisieren. Eine Funktionsanweisung kann nur einen Ausdruck umfassen und gilt nur in der Programmeinheit in der sie definiert wurde.
Definieren einer Funktionsanweisung:
funktionsname( [formale parameter]) = ausdruck
|
Aufruf der Funktion:
[variable = ] funktionsname( [aktuelle parameter])
|
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP FUNK() = 5 WRITE (*,*) FUNK () C Ausgabe: 5.000000 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP REAL A, B, C FUNK(A, B) = COS(A) * LOG(B) C = FUNK(3.1415, 2.) WRITE (*,*) C C Ausgabe: -0.6931472 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
FUNCTIONBearbeiten
Soll eine Funktion mehrere Anweisungen umfassen, so genügt das Konzept der Funktionsanweisung nicht mehr. FORTRAN 77 kennt zu diesem Zweck das Schlüsselwort FUNCTION
.
[datentyp] anweisungen
|
Aufgerufen wird eine derartige Funktion gleich wie eine Funktionsanweisung.
Beispiel:
Datei bsp.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP C Funktionsaufruf WRITE(*,*) FUNK() C Ausgabe: 27.50000 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei funk.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
REAL FUNCTION FUNK() REAL TMP DO 10 I = 1,10 TMP = TMP + I*0.5 10 CONTINUE FUNK = TMP END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Übersetzung mit gfortran:
gfortran bsp.f funk.f
Eine Funktion muss einen Wert zurückgeben. Welcher Wert das ist, wird durch eine Zuweisung an den Funktionsnamen erreicht. Wird am Anfang des Funktionskopfes kein Datentyp explizit vorgegeben, so gelten die Regeln für die implizite Datentypvergabe.
Mit Hilfe des Schlüsselwortes RETURN
kann eine Funktion auch vor dem Funktionsende verlassen werden.
Beispiel:
Datei bsp.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP C Funktionsaufruf WRITE(*,*) FUNK(3) C Ausgabe: 1.500000 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei funk.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
FUNCTION FUNK(I1) IF(I1 .LE. 5) THEN FUNK = 1.5 RETURN END IF FUNK = SIN(I1*0.5) END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
SUBROUTINEBearbeiten
Eine Subroutine besitzt im Gegensatz zu einer Funktion keinen Datentyp und gibt keinen Wert zurück.
anweisungen
|
Aufruf der Subroutine:
CALL subroutinenname( [aktuelle parameter])
|
Beispiel:
Datei test.f
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CALL SUB END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei sub.f
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
SUBROUTINE SUB WRITE (*,*) 'Hallo Welt!' END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Übersetzung mittels gfortran:
gfortran -c sub.f gfortran -c test.f gfortran test.o sub.o
Anzeige auf der Standardausgabe:
Hallo Welt!
Auch eine Subroutine kann mittels RETURN
vorzeitig verlassen werden.
Die aktuellen und formalen Parameter müssen hinsichtlich Datentyp, Anzahl, Reihenfolge übereinstimmen.
Alle Namen und Variablen in einer Programmeinheit (Subroutine, Funktion oder Hauptprogramm) sind grundsätzlich nur lokal in der jeweiligen Programmeinheit bekannt. Über die Unterprogrammparameter können aber sehr wohl Werte in der aufrufenden Programmeinheit geändert werden.
Beispiel:
Datei bsp.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP REAL A = 2.0 CALL SUB(A) WRITE(*,*) A C Ausgabe: 10 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei sub.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
SUBROUTINE SUB(X) REAL X REAL A C Unterprogrammparameter X = 10 C lokale Variable A = 500 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Felder als ParameterBearbeiten
Beispiel: Übergabe eines ganzen Feldes
Datei bsp.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER FELD(3,3) INTEGER CNT CNT = 1 DO 10 I = 1, 3 DO 20 J = 1, 3 FELD(J,I) = CNT CNT = 1 + CNT 20 CONTINUE 10 CONTINUE C Unterprogrammaufruf CALL SUB(FELD) C Ausgabe: 1 2 3 4 5 6 7 8 9 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei sub.f
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
SUBROUTINE SUB(ARR) INTEGER ARR(3, 3) WRITE(*,*) ARR END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Beispiel: Übergabe einer Feld-Teilkette
Datei bsp.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER FELD(3,3) INTEGER CNT CNT = 1 DO 10 I = 1, 3 DO 20 J = 1, 3 FELD(J,I) = CNT CNT = 1 + CNT 20 CONTINUE 10 CONTINUE C Unterprogrammaufruf CALL SUB(FELD(1:2,2:3)) C Ausgabe: 4 5 7 8 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei sub.f
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
SUBROUTINE SUB(ARR) INTEGER ARR(0:1, 0:1) WRITE(*,*) ARR END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Beispiel: Übergabe eines Feld-Einzelelements
Datei bsp.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER FELD(3,3) INTEGER CNT CNT = 1 DO 10 I = 1, 3 DO 20 J = 1, 3 FELD(J,I) = CNT CNT = 1 + CNT 20 CONTINUE 10 CONTINUE C Unterprogrammaufruf CALL SUB(FELD(1,2)) C Ausgabe: 4 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei sub.f
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
SUBROUTINE SUB(ARR) INTEGER ARR WRITE(*,*) ARR END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Prozeduren als ParameterBearbeiten
Auch Prozeduren können als Parameter übergeben werden.
Standardfunktionen werden dazu folgendermaßen im Vereinbarungsteil gekennzeichnet:
Aufruf der Subroutine:
INTRINSIC namensliste
|
Eigene Funktionen oder Subroutinen mit:
EXTERNAL namensliste
|
Beispiel:
Datei bsp.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP REAL PI PARAMETER(PI=3.1415927) C intrinsic functions INTRINSIC SIN, COS C Unterprogrammaufrufe CALL SUB(SIN, PI) C Ausgabe: 0.000000 CALL SUB(COS, PI) C Ausgabe: -1.000000 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei sub.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
SUBROUTINE SUB(FUNK, X) REAL FUNK, X WRITE(*,*) NINT(FUNK(X)*1000)/1000.0 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
COMMONBearbeiten
Mit COMMON
läßt sich ein gemeinsamer Datenbereich für mehrere Programmeinheiten realisieren.
Unbenannter COMMON:
COMMON variablenliste
|
COMMON / name/ variablenliste
|
Beispiel:
Datei bsp.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP REAL A, B, C, D COMMON A, B, C COMMON /C1/ D A = 4.0 B = 5.0 C = 6.0 CALL SUB WRITE (*,*) A, B, C, D C Ausgabe: 3.330000 4.440000 6.000000 5.550000 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei sub.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
SUBROUTINE SUB REAL X, Y, Z COMMON X, Y COMMON /C1/ Z X = 3.33 Y = 4.44 Z = 5.55 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
ENTRYBearbeiten
Mittels ENTRY
kann gezielt in ein Unterprogamm gesprungen werden. Dieses Konstrukt widerspricht aber einer strukturierten Programmierung und sollte nicht verwendet werden.
ENTRY entryname[( [formale parameter]) ]
|
Der Aufruf entspricht dem einer Subroutine:
CALL entryname[( [aktuelle parameter]) ]
|
Beispiel:
Datei bsp.f
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CALL SUB C Ausgabe: Hallo C Welt! CALL E1 C Ausgabe: Welt! END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei sub.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
SUBROUTINE SUB WRITE(*,*) 'Hallo' ENTRY E1 WRITE (*,*) 'Welt!' END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
SAVEBearbeiten
Durch ein SAVE
-Statement in Unterprogrammen behalten die lokalen Variablen ihren jeweiligen Wert auch nach Verlassen des Unterprogrammes. Dieses Konstrukt ist meist nicht notwendig, da die meisten FORTRAN-Compiler dieses Verhalten ohnehin automatisch aufweisen (siehe auch Kapitel DATA zwecks Initialisierung von Variablen).
SAVE [variablenliste]
|
Beispiel:
Datei bsp.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CALL SUB C Ausgabe: 1.000000 CALL SUB C Ausgabe: 2.000000 CALL SUB C Ausgabe: 3.000000 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei sub.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
SUBROUTINE SUB REAL A SAVE A = A + 1 WRITE(*,*) A END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
DATABearbeiten
DATA
dient zur Wertinitialisierung von Variablen vor der Programmeinheitausführung. Diese Anweisung ist also nicht gleichzusetzen mit einer Wertzuweisung.
Beispiel:
DATA [variablenliste] / variablenwerte/
|
Beispiel:
Datei bsp.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CALL SUB C Ausgabe: 1.000000 CALL SUB C Ausgabe: 2.000000 CALL SUB C Ausgabe: 3.000000 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei sub.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
SUBROUTINE SUB REAL A DATA A /0.0/ A = A + 1 WRITE(*,*) A END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Unterschied zwischen Wertinitialisierung und Wertzuweisung:
Wertinitialisierung | Wertzuweisung | |
---|---|---|
Code |
PROGRAM BSP
CALL SUB
CALL SUB
CALL SUB
END
SUBROUTINE SUB
REAL A
DATA A /3.0/
A = A + 1
WRITE(*,*) A
END
|
PROGRAM BSP
CALL SUB
CALL SUB
CALL SUB
END
SUBROUTINE SUB
REAL A
A = 3.0
A = A + 1
WRITE(*,*) A
END
|
Ausgabe |
4.000000 5.000000 6.000000 |
4.000000 4.000000 4.000000 |
Ein- und AusgabeBearbeiten
READBearbeiten
Die READ
-Anweisung dient dem Einlesen von Daten. Typisches Beispiel ist die Dateneingabe mittels Tastatur. Formal sieht eine READ
-Anweisung so aus:
READ([UNIT=]unit, [FMT=]fmt [, ERR=err] [, END=end] [, IOSTAT=iostat]) [eingabeliste] |
- unit ... Nummer der Eingabeeinheit (ist systemabhängig), Sternoperator oder auch die einer Datei mittels
OPEN
-Anweisung zugeordnete Nummer. - fmt ... Anweisungsnummer zu einer
FORMAT
-Anweisung oder Sternoperator - err ... Tritt während der Eingabe ein Fehler auf, so wird zu dieser Anweisungsnummer gesprungen
- end ... Nach dem Einlesen des letzten Datensatzes wird zu dieser Anweisungsnummer gesprungen
- iostat ... READ-Status
Listengesteuerte Eingabe auf der Standardeingabe (normalerweise die Tastatur):
READ (*,*) A, B, C
Alternativ kann das auch so geschrieben werden:
READ (UNIT=*, FMT=*) A, B, C
Beim Intel Fortran Compiler, gfortran und g95 ist auch UNIT = 5
als stdin
(Tastatur) vorbelegt. Das Einlesen aus Dateien und die Einstellung des Formates werden später erläutert.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I(5) C Einlesen in ein Feld (UNIT ... Standardeingabe, FMT ... listengesteuert) READ (*,*) I C ... END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Kurze Erläuterung zu IOSTAT:
Wert | Erläuterung |
---|---|
0 | kein Fehler |
positiver Wert (systemabhängig) | Fehler |
negativer Wert (systemabhängig) | End Of File und kein Fehler |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I INTEGER ST C Einlesen eines Wertes READ (*, *, IOSTAT=ST) I C Ausgabe des IO-Status WRITE (*,*) 'IO-Status:', ST END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
- Ausgabe:
- Für Eingabe: 5 → 0
- Für Eingabe: 5.3 → Positiver Wert = Fehler
WRITEBearbeiten
Die WRITE
-Anweisung dient der Datenausgabe. Typisches Beispiel ist die Anzeige von Daten auf dem Bildschirm. Formal sieht eine WRITE
-Anweisung so aus:
WRITE([UNIT=]unit, [FMT=]fmt [, ERR=err] [, IOSTAT=iostat]) [ausgabeliste] |
- unit ... Nummer der Ausgabeeinheit (ist systemabhängig), Sternoperator oder auch die einer Datei mittels
OPEN
-Anweisung zugeordnete Nummer. - fmt ... Anweisungsnummer zu einer
FORMAT
-Anweisung oder Sternoperator - err ... Tritt während der Ausgabe ein Fehler auf, so wird zu dieser Anweisungsnummer gesprungen
- iostat ... WRITE-Status
Listengesteuerte Ausgabe auf der Standardausgabe (normalerweise der Bildschirm):
WRITE (*,*) A, B, C
Alternativ kann das auch so geschrieben werden:
WRITE (UNIT=*, FMT=*) A, B, C
Beim Intel Fortran Compiler, gfortran und g95 sind auch
unit=0
alsstderr
(Bildschirm) undunit=6
alsstdout
(Bildschirm)
vorbelegt. Bezüglich IOSTAT
gilt auch hier der im vorigen Abschnitt kurz geschilderte Sachverhalt.
Die Ausgabe in Dateien und die Einstellung des Formates werden nachfolgend erläutert.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I(5) C I(1) = ... C ... C Ausgabe der Feldwerte (UNIT ... Standardausgabe, FMT ... listengesteuert) WRITE (*,*) I C ... END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
FormatierungBearbeiten
Die Ein- und Ausgabeformatierung kann beeinflusst werden. Zu diesem Zweck gibt es die FORMAT
-Anweisung.
... (..., FMT = marke, ...) ... marke FORMAT (formatliste) |
Alternativ dazu kann auch direkt bei der FMT
-Option die Formatliste bekanntgemacht werden.
... (..., FMT = '(formatliste)', ...) ... |
FormatlistenelementeBearbeiten
Formatspezifizierer | Kommentar |
---|---|
Ix[.z] | Ganzzahl mit einer Feldlänge von x Zeichen. z gibt die Mindestanzahl der auszugebenden Zeichen an (Feld wird, wenn nötig, mit führenden Nullen aufgefüllt). |
Fx.y | Fixkommazahl mit einer Gesamtfeldlänge von x Zeichen. y ist die Anzahl der Nachkommastellen (Vorzeichen und Dezimalpunkt müssen in der Gesamtfeldlänge berücksichtigt werden). |
Ex.y | Gleitkommazahl mit einer Gesamtfeldlänge von x Zeichen. y ist die Anzahl der Nachkommastellen. (Vorzeichen, Dezimalpunkt und die Zeichen für den Exponenten müssen in der Gesamtfeldlänge berücksichtigt werden). |
Dx.y | -"- |
A | Ein Zeichenkette. |
Ax | Eine Zeichenkette mit x Zeichen. |
Lx | Ein logischer Wert, T bzw. F |
xX | x Leerzeichen. |
Obige Tabelle der Formatlistenelemente ist nicht vollständig. Fortran kennt noch weitere Formatierungsmöglichkeiten. Die Ausgabe erfolgt normalerweise rechtsbündig. Reicht die Gesamtfeldlänge bei numerischen Werten nicht aus, so werden anstelle einer Zahl Sternchen angezeigt.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER A A = 999 WRITE (*, 3333) A C Ausgabe: 999 A = -999 WRITE (*, 3333) A C Ausgabe: *** 3333 FORMAT (I3) END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Weitere Formatierungsbeispiele:
Code | Ausgabe |
---|---|
WRITE(*, 999) 1234 WRITE(*, 999) 1234567 WRITE(*, 999) 1234567890 999 FORMAT(I9.6) |
001234 1234567 ********* |
WRITE(*, 999) 555.6666 WRITE(*, 999) +5.6 WRITE(*, 999) -55.666E7 WRITE(*, 999) -55555.666 999 FORMAT(F9.3) |
555.667 5.600 ********* ********* |
WRITE(*, 999) 555.6666 WRITE(*, 999) +5.6 WRITE(*, 999) -55.666E7 WRITE(*, 999) -55555.666 999 FORMAT(E9.3) |
0.556E+03 0.560E+01 -.557E+09 -.556E+05 |
WRITE(*, 999) 'Hallo' WRITE(*, 999) 'ABCDEFGHIJKL' WRITE(*, 888) 'ABCDEFGHIJKL' 888 FORMAT(A) 999 FORMAT(A10) |
Hallo ABCDEFGHIJ ABCDEFGHIJKL |
WRITE(*, *) 'FORTRAN', '77' WRITE(*, 999) 'FORTRAN', '77' 999 FORMAT(A, 1X, A) |
FORTRAN77 FORTRAN 77 |
WRITE(*, 888) 'FORTRAN', '77' WRITE(*, 999) 'FORTRAN', '77' 888 FORMAT(A, T3, A) 999 FORMAT(A, T20, A) |
FO77RAN FORTRAN 77 |
WRITE(*, 999) 'FORTRAN', '77' 999 FORMAT(A, /, A) |
FORTRAN 77 |
WRITE(*, 999) 34.56 WRITE(*, *) 34.56 C SP ... Sign Plus (+) 999 FORMAT(SP, F12.3) |
+34.560 34.56 |
Wiederholung von FormatteilenBearbeiten
Beispiel:
WRITE (*, 100) 'abc', 10.3, 'xxx', 23.4 100 FORMAT (2(A3, F6.1))
WRITE etwas andersBearbeiten
Beispiel:
WRITE (*, 100) 100 FORMAT ('Hallo', 1X, 'Welt!')
DateienBearbeiten
DatensatzBearbeiten
Datensätze können in folgender Form auftreten:
- Formatierter Datensatz: Textdatensatz
- Unformatierter Datensatz: Datensatz in einer maschineninternen Form.
- Dateiendesatz
DateiBearbeiten
Für FORTRAN 77 ist alles eine Datei, das durch READ oder WRITE bearbeitbar ist.
Zugriffsmethoden:
- Sequentieller Zugriff: Lesen ab Beginn der Datei (file) und dann immer den nächsten Datensatz einlesen. Geschrieben wird jeweils ans Dateiende. Auf interne Dateien kann nur sequentiell zugegriffen werden.
- Direkter Zugriff: Bearbeiten in beliebiger Reihenfolge durch Angabe der Satznummer.
Dateitypen:
- Externe Datei: Eine konventionelle Datei
- Interne Datei: CHARACTER-Variable oder -Feld.
Dateien haben im Betriebssystem einen Dateinamen. In FORTRAN wird eine Datei über eine Dateinummer (unit) angesprochen. Die Zuordnung erfolgt mit dem Befehl OPEN
.
OPENBearbeiten
Zum Öffnen einer externen Datei dient die OPEN
-Anweisung.
OPEN (liste) |
mit folgender liste
Element | Kommentar |
---|---|
[UNIT =] x | x ist eine Dateinummer (0 bis 99) |
FILE = x | x ist der externe Dateiname |
IOSTAT = x | x ist 0 wenn OPEN fehlerfrei ausgeführt wurde, ansonsten eine systemabhängige Fehlernummer |
ERR = x | Bei Fehler Sprung zur Anweisungsnummer x |
STATUS = x | Dateistatus: 'OLD' ... Datei existiert bereits 'NEW' ... Datei wird neu erzeugt 'REPLACE' ... Wenn die Datei existiert, wird sie überschrieben, ansonsten neu erzeugt. 'SCRATCH' ... namenlose temporäre Datei 'UNKNOWN' ... System bestimmt Dateistatus selbst |
ACCESS = x | Zugriffsmethode: 'SEQUENTIAL' ... Sequentielle Datei 'DIRECT' ... direkter Zugriff |
FORM = x | Format: 'FORMATTED' oder 'UNFORMATTED' |
RECL = x | Datensatzlänge (positive Zahl, ACCESS='DIRECT', in Bytes bzw. bei formatierten Dateien in Characters) |
BLANK = x | 'NULL' (ignorieren von Leerzeichen bei numerischen Werten) oder 'ZERO' (Leerzeichen bei numerischen Werten als 0 interpretieren) |
Eingestellte Vorgabewerte sind:
- STATUS = 'UNKNOWN'
- ACCESS = 'SEQUENTIAL'
- FORM = 'FORMATTED'
- BLANK = 'NULL'
Wird ACCESS='DIRECT'
gesetzt, so gilt FORM='UNFORMATTED'
als Vorgabewert.
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP OPEN (20, FILE='/tmp/testdatei.txt', STATUS='OLD', ERR=222) WRITE (*,*) 'Voller Erfolg' CLOSE(20) GOTO 333 222 WRITE(*,*) 'Fehler beim Öffnen der Datei' 333 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
CLOSEBearbeiten
Geschlossen wird die Verbindung zur externen Datei mit dem CLOSE
-Befehl.
CLOSE (liste) |
liste:
Element | Kommentar |
---|---|
[UNIT =] x | wie bei OPEN |
IOSTAT = x | wie bei OPEN |
ERR = x | wie bei OPEN |
STATUS = x | KEEP ... Datei erhalten DELETE ... Datei löschen |
Lesen und SchreibenBearbeiten
Aus Dateien gelesen oder in Dateien geschrieben wird mit den bereits bekannten READ
- und WRITE
-Anweisungen.
Element | Kommentar |
---|---|
[UNIT =] x | Unit 0 bis 99 bzw. CHARACTER-Variable oder Feld (interne Datei) |
[FMT =] x | siehe Formatierung |
REC = x | Datensatznummer bei Direktzugriff (siehe Abschnitt Direktzugriff) |
IOSTAT = x | wie bei READ
|
ERR = x | Bei Fehler Sprung zur Anweisungsnummer x |
END = x | Bei Dateiende Sprung zur Anweisungsnummer x (nicht erlaubt bei Direktzugriff) |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*80 A OPEN (20, FILE='/tmp/testdatei.txt', STATUS='OLD', ERR=222) 10 CONTINUE C Aus Datei lesen READ (20, 888, END=20) A C Auf Standardausgabe schreiben WRITE (*,*) A GOTO 10 20 CLOSE(20) GOTO 333 222 WRITE(*,*) 'Fehler beim Öffnen der Datei' 888 FORMAT(A) 333 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
DirektzugriffBearbeiten
OPEN:
Element | Kommentar |
---|---|
ACCESS = x | x ... 'DIRECT' |
RECL = x | x ... Datensatzlänge (positive Zahl, ACCESS='DIRECT', in Bytes bzw. bei formatierten Dateien in Characters) |
READ/WRITE:
Element | Kommentar |
---|---|
REC = x | x ... Satznummer bei Direktzugriff |
Beispiel: Gegeben ist die Textdatei /tmp/testdatei.txt
mit dem Inhalt
Die WRITE-Anweisung dient der Datenausgabe aus einem FORTRAN-Programm auf ein externes Gerät. Typisches Beispiel ist die Anzeige von Daten auf dem Bildschirm. Formal sieht eine WRITE-Anweisung so aus:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*10 C OPEN (20, FILE='/tmp/testdatei.txt', & STATUS='OLD', & ACCESS='DIRECT', & RECL=10, & ERR=222) READ (20, REC=4, ERR=333) C WRITE (*,*) C READ (20, REC=25, ERR=333) C WRITE (*,*) C CLOSE (20) GOTO 444 222 WRITE (*,*) 'Fehler beim Öffnen der Datei' 333 WRITE (*,*) 'Fehler beim Lesen des Datensatzes' 444 CONTINUE END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Ausgabe:
Datenausga Fehler beim Lesen des Datensatzes
Positionieren bei sequentiellen DateienBearbeiten
Datensatzzeiger um einen Datensatz zurücksetzen:
BACKSPACE ([UNIT=]x [,IOSTAT=y] [,ERR=z]) |
Positionieren an den Dateibeginn:
REWIND ([UNIT=]x [,IOSTAT=y] [,ERR=z]) |
Schreiben eines Dateiendsatzes:
ENDFILE ([UNIT=]x [,IOSTAT=y] [,ERR=z]) |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*100 C(3) OPEN (20, FILE='/tmp/testx.txt', & STATUS='NEW', & ERR=222) WRITE (20,*) 'Das ist eine Testdatei' WRITE (20,*) 'Dies ist Zeile 2 der Testdatei' WRITE (20,*) 'Jenes die Zeile 3 der Testdatei' WRITE (20,*) 'Jetzt ist''s aber genug' ENDFILE (20, ERR=444) REWIND (20, ERR=444) READ (20, FMT=555, ERR=333) C WRITE (*, FMT=555) C BACKSPACE (20, ERR=444) READ (20, FMT=555, ERR=333) C(1) WRITE (*, FMT=555) C(1) GOTO 999 222 WRITE (*,*) 'Fehler beim Öffnen der Datei' GOTO 999 333 WRITE (*,*) 'Fehler beim Lesen des Datensatzes' GOTO 999 444 WRITE (*,*) 'Sonstiger Fehler' GOTO 999 555 FORMAT (A) 999 CLOSE (20) C Ausgabe: C Das ist eine Testdatei C Dies ist Zeile 2 der Testdatei C Jenes die Zeile 3 der Testdatei C Jenes die Zeile 3 der Testdatei END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
INQUIREBearbeiten
Die Anweisung INQUIRE
dient der Abfrage einiger Eigenschaften von Dateien oder I/O-Units.
INQUIRE (FILE = x, liste) |
mit x ... Dateiname (inkl. Pfad)
INQUIRE ([UNIT =] x, liste) |
mit x ... Nummer der I/O-Unit.
liste:
Element | Kommentar |
---|---|
ACCESS = x | x:
|
BLANK = x | x:
|
DIRECT = x | x:
|
ERR = x | Bei Fehler Sprung zur Anweisungsnummer x |
EXIST = x | x:
|
FORM = x | x:
|
FORMATTED = x |
|
IOSTAT = x | x ist 0 wenn INQUIRE fehlerfrei ausgeführt wurde, ansonsten eine systemabhängige positive Fehlernummer
|
NAME = x | Der Dateiname wird der Zeichenketten-Variablen x zugewiesen. Hat die Datei keinen Namen, dann ist das Ergebnis undefiniert. |
NAMED = x | x:
|
NEXTREC = x | x ... Nummer des nächsten Datensatzes |
NUMBER = x | x ... Nummer der mit einer externen Datei verbundenen I/O-Unit. |
OPENED = x | x:
|
RECL = x | x ... Datensatzlänge bei Direktzugriff |
SEQUENTIAL = x | x:
|
UNFORMATTED = x |
|
Beispiel: Datei vorhanden?
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP LOGICAL L INQUIRE (FILE='/tmp/testdatei.txt', EXIST=L, ERR=222) WRITE (*,*) L C Ausgabe: C wenn Datei existiert: T C wenn Datei nicht existiert: F GOTO 999 222 WRITE (*,*) 'Fehler!' 999 CONTINUE END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Beispiel: Infos zu einer geöffneten Datei
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP LOGICAL EX CHARACTER*15, DI, FO, AC, SE INTEGER NU OPEN (25, FILE='/tmp/testdatei.txt', STATUS='OLD', ERR=222) INQUIRE (25, EXIST = EX, & DIRECT = DI, & SEQUENTIAL = SE, & FORMATTED = FO, & ACCESS = AC, & NUMBER = NU, & ERR=222) WRITE (*,*) 'EXIST? ', EX WRITE (*,*) 'DIRECT? ', DI WRITE (*,*) 'SEQUENTIAL? ', SE WRITE (*,*) 'FORMATTED? ', FO WRITE (*,*) 'ACCESS? ', AC WRITE (*,*) 'NUMBER? ', NU C Ausgabe, z.B. C EXIST? T C DIRECT? YES C SEQUENTIAL? YES C FORMATTED? YES C ACCESS? SEQUENTIAL C NUMBER? 25 GOTO 999 222 WRITE (*,*) 'Fehler!' 999 CLOSE (25) END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Interne DateienBearbeiten
- Interne Dateien sind vom Datentyp CHARACTER (Zeichen oder Zeichenketten)
- Das Lesen aus bzw. das Schreiben in interne Dateien erfolgt immer sequentiell
Beispiel: Schreiben in eine interne Datei
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*15 CH REAL R R = 12.5678 C Interne Datei "CH" WRITE (CH, *) R WRITE (*,*) 'R lexikalisch groesser als Buchstabe "A"? ', & LGE(CH, 'A') END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Beispiel: Lesen aus einer internen Datei
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*15 CH REAL R CH = '12.5678' C Interne Datei "CH" READ (CH, '(F15.5)') R WRITE (*,*) 'R = ', R WRITE (*,*) 'R**2 = ', R**2 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
AnhangBearbeiten
PAUSEBearbeiten
Die PAUSE
-Anweisung unterbricht die Programmausführung. Diese Anweisung wurde mit dem Fortran 90/95-Standard aus dem Fortran-Sprachumfang verbannt. Moderne Compiler geben deshalb teilweise bei Verwendung dieser Anweisung eine Warnmeldung (obsolete o.ä.) aus. Das Verfahren zur normalen Fortsetzung des infolge PAUSE
-Anweisung angehaltenen Programmes ist compilerabhängig, z. B.:
- gfortran und g77:
go
RETURN - g95: RETURN
- Intel Fortran Compiler 9.0:
continue
RETURN
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP WRITE (*,*) 'Hallo ' PAUSE WRITE (*,*) 'Welt!' END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Ausgabe bei Compilierung mit gfortran:
Hallo PAUSE To resume execution, type go. Other input will terminate the job.
Ausgabe bei Compilierung mit g95:
Hallo PAUSE statement executed. Hit Return to continue
Ausgabe bei Compilierung mit ifort:
Hallo FORTRAN PAUSE PAUSE prompt>
INCLUDEBearbeiten
INCLUDE
ermöglicht das Einbinden einer Datei. INCLUDE
ist nicht explizit im FORTRAN 77-Standard angeführt, sondern wurde 1978 als Erweiterung des FORTRAN-Standards vom US-amerikanischen DoD im MilStd 1753 festgelegt (siehe auch Abschnitt MilStd 1753).
Beispiel:
Datei bsp.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INCLUDE 'inc.f' WRITE(*,*) 'Fläche ist', 10**2*PI, 'cm2' C Ausgabe: Fläche ist 314.1593 cm2 END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Datei inc.f:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
REAL PI PARAMETER(PI=3.141593) |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Hollerith-KonstantenBearbeiten
Eine veraltete Möglichkeit Zeichenketten anzugeben stellen die Hollerith[1]-Konstanten dar. Eine Hollerith-Konstante besteht aus
- Einer positiven Ganzzahl, welche die Zeichenkettenlänge angibt
- Dem Buchstaben H
- Der Zeichenkette
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP CHARACTER*5 C DATA C /5HUralt/ WRITE (*,*) C C Ausgabe: Uralt END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
Arithmetisches IFBearbeiten
Schon in grauer Vorzeit fanden Programmierer arithmetische IF-Anweisungen sehr unterhaltsam, weil damit der Programmcode interessanter gestaltet werden konnte (Quelle: Ed Post - Real Programmers Don't Use PASCAL). Diese Aussage ist begreiflicherweise auch heute noch uneingeschränkt gültig.
IF(ausdruck) ziel1, ziel2, ziel3 |
mit
ausdruck | springe zu |
---|---|
< 0 | ziel1 |
= 0 | ziel2 |
> 0 | ziel3 |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER I I = -55 IF(I) 100, 200, 300 GOTO 999 100 WRITE(*,*) "Negative Zahl" C Ausgabe: Negative Zahl GOTO 999 200 WRITE(*,*) "Null" GOTO 999 300 WRITE(*,*) "Positive Zahl" 999 CONTINUE END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
ASSIGN und Assigned GOTOBearbeiten
Noch wesentlich amüsanter als bei der unachtsamen Verwendung des arithmetischen IF können die Ergebnisse durch Verwendung von Assign-Anweisungen ausfallen.
ASSIGN weist eine Zielmarke einer INTEGER-Variablen zu:
ASSIGN zielmarke TO variable |
Verwendung finden kann dies neben anderem beim Assigned GOTO:
GOTO variable (liste) |
Beispiel:
0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
PROGRAM BSP INTEGER VAR C ... ASSIGN 200 TO VAR C ... C ... C ... GOTO VAR (100, 200, 300) 100 WRITE(*,*) "Negative Zahl" GOTO 999 200 WRITE(*,*) "Null" C Ausgabe: Null GOTO 999 300 WRITE(*,*) "Positive Zahl" 999 CONTINUE END |
12345678901234567890123456789012345678901234567890123456789012345678901234567890 0 . | 1 . 2 . 3 . 4 . 5 . 6 . 7 | . 8 |
MilStd 1753, DoD Supplement To American National Standard X3.9-1978Bearbeiten
Nachfolgend stichwortartig die vom amerikanischen Department of Defense im Jahr 1978 ergänzend zu FORTRAN 77 geforderten Spracheigenschaften
- END DO (gelabelt, für DO-Schleifen)
- DO WHILE-Schleife
- INCLUDE
- IMPLICIT NONE
- Manipulation von Bitfeldern (IOR, IAND,ISHFT, ...)
- READ/WRITE-Verhalten nach EOF.
- ↑ Herman Hollerith (* 1860, † 1929), Erfinder des Hollerith-Lochkartenverfahrens, Herman Hollerith
ProgrammaufbauBearbeiten
Beispiel: Hallo WeltBearbeiten
Fortran 90/95-Code (free source form) |
program hallo ! Das typische "Hallo Welt"-Programm write( *, * ) 'Hallo Welt!' end program hallo |
Dieses Programm besteht aus den gleichen Anweisungen wie ein entsprechendes FORTRAN 77-Programm und wurde in der sogenannten "free source form" verfasst. Aus diesem Grund ist die erste Spalte nicht mehr dem Kommentarzeichen vorbehalten. Kommentare werden hier durch ein Rufzeichen eingeleitet. Alles rechts vom Rufzeichen wird als Kommentar behandelt. Ein Kommentar kann also auch nach einer Anweisung stehen. Im Gegensatz zu einem FORTRAN 77-Programm wird in Fortran 90/95 oft die Kleinschreibung bevorzugt. Dies ist aber nicht zwingend erforderlich, da die Fortran-Compiler case-insensitiv sind. D.h. sie unterscheiden nicht zwischen Groß- und Kleinschreibung. Eine Einrückung von Blöcken ist nicht zwingend erforderlich, fördert aber die Übersichtlichkeit des Programmcodes.
Die Anweisung in der ersten Zeile kennzeichnet die Programmeinheit als Hauptprogramm und gibt ihr die Bezeichnung hallo
. Es folgt eine Kommentarzeile. Dann folgt die Anweisung, einen String auf die Standardausgabe zu schreiben. Und schließlich signalisiert die end
-Anweisung das Programmende.
Das ZeilenformatBearbeiten
Fortran 90/95 bietet zwei verschiedene Programmaufbaumöglichkeiten:
- free source form
- fixed source form
Die "free source form" ist neu in Fortran 90/95. Sie bietet die Möglichkeit Programme ohne fixe Spaltenzuordnung zu schreiben, wie dies auch in den meisten anderen gebräuchlichen Programmiersprachen üblich ist. Zusätzlich ist in Fortran 90/95 aus Kompatibilitätsgründen auch das alte FORTRAN 77-Zeilenformat (fixed source form) enthalten. Dieses sollte in neuen Programmen aber nicht mehr verwendet werden.
Normalerweise gilt, dass jede Fortran-Anweisung in einer eigenen Zeile steht. Bei Verwendung der "free source form" gelten folgende Bedingungen. Eine Zeile darf maximal 132 Zeichen lang sein. Als Zeilenumbruchzeichen dient das Kaufmanns-Und (&). Das Kaufmanns-Und steht in diesem Fall immer am Ende der fortzuführenden Zeile, optional auch zusätzlich am Beginn der Fortsetzungszeile. Standardmäßig sind maximal 40 Fortsetzungszeilen erlaubt.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp ! Leerzeilen werden vom Compiler ignoriert ! Vereinbarungsteil implicit none ! implizite Datentypvereinbarung ausschalten integer :: a, b, c ! Variablendeklaration (Ganzzahlen) character(25) :: str ! Variablendeklaration (String mit einer Länge von 25 Zeichen) ! Aktionsteil a = 5 b = 7 c = a + & ! und jetzt kommt eine Fortsetzungszeile b write( *, * ) c ! auch Strings oder Schlüsselwörter können auf der nächsten Zeile fortgesetzt werden. ! Dabei ist unbedingt darauf zu achten, dass die Fortsetzungszeile auch mit einem ! Kaufmanns-Und eingeleitet wird, da ansonsten einige Compiler Fehlermeldungen liefern str = "Hal& &lo Welt!" wr& &ite( *, * ) str ! Ausgabe: ! 12 ! Hallo Welt! end program bsp |
Mehrere Anweisungen können in eine Zeile geschrieben werden, wenn eine Trennung durch jeweils ein Semikolon erfolgt.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: a, b, c a = 5; b = 7; c = a + b write( *, * ) c ! Ausgabe: ! 12 end program bsp |
Die Programmstruktur für das HauptprogrammBearbeiten
Ein Hauptprogramm weist in einfachster Darstellung immer folgende Struktur auf:
program
Name- Vereinbarungsteil
- Aktionsteil
end
[program
[Name]]
Die in eckigen Klammern angegebenen Bestandteile sind optional. Sie sollten dennoch stets mit angegeben werden. Genau eine end
-Anweisung (mit oder ohne optionale Bestandteile) ist obligatorisch. Bei Hauptprogramm und Unterprogrammen ist end
eine ausführbare Anweisung. Ein Fortran-Programm darf genau ein Hauptprogramm enthalten, es ist der Startbereich für die Programmausführung.
Der Fortran-ZeichenvorratBearbeiten
Fortran 95-Programme bestehen standardmäßig aus folgenden Zeichen:
- Großbuchstaben: A bis Z
- Kleinbuchstaben: a bis z
- Ziffern: 0 bis 9
- Den 13 FORTRAN 77-Sonderzeichen: + - * / = ( ) : , . ' $ und dem Leerzeichen
- Unterstrich (Underscore): _
- Weitere Sonderzeichen: ! ? " & ; < >
Ein Fortran 90/95-Compiler ist case-insensitiv: er unterscheidet nicht zwischen Groß- und Kleinbuchstaben. Stringkonstanten können natürlich alle ASCII-Zeichen beinhalten.
Symbolische NamenBearbeiten
Standardmäßig dürfen symbolische Namen maximal 31 Zeichen lang sein. Das erste Zeichen muss immer ein Buchstabe sein. Anschließend sind alphanumerische Zeichen (Buchstabe oder Ziffer) und Unterstriche erlaubt. Im Gegensatz zu FORTRAN 77 dürfen nun keine Leerzeichen innerhalb eines symbolischen Namens auftreten.
Reservierte Schlüsselwörter?Bearbeiten
Im Fortran 95-Standard ist zwar die Rede von Schlüsselwörtern (statement keywords), wie z.B. if, do, real, write
. Allerdings wird eindeutig darauf hingewiesen, dass diese nicht reserviert sind. Das heißt, solche Schlüsselwörter können auch für eigene Bezeichner verwendet werden.
Beispiel:
Folgender Programmcode ist in Fortran 90/95 gültig, aber nicht empfehlenswert
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: integer, write, if integer = 6 write = 5 if = 1 write( *, * ) integer + write + if end program bsp |
Details zur Anordnungsreihenfolge von AnweisungenBearbeiten
Fortran schreibt eine gewisse Anordnungsstruktur der einzelnen Programmelemente vor. So ist das folgende Programm
Fortran 90/95-Code (free source form) |
program hallo write( *, * ) "Hallo Welt" implicit none end program hallo |
nicht standardkonform, da die implicit none
-Vereinbarung nicht nach einer ausführbaren Anweisung folgen darf. So müsste es richtig lauten
Fortran 90/95-Code (free source form) |
program hallo implicit none write( *, * ) "Hallo Welt" end program hallo |
Im Fortran 90/95-Standard ist genau festgelegt wann bestimmte Programmelemente auftreten dürfen. Prinzipiell gilt, dass Vereinbarungs-Anweisungen (nichtausführbare Anweisungen) vor Aktions-Anweisungen (ausführbaren Anweisungen) stehen. Doch es gibt Ausnahmen, z.B. darf die nichtexekutierbare format
-Anweisung auch zwischen oder nach ausführbaren Anweisungen stehen. Eindeutig ist Folgendes geregelt. Die Programmeinheit beginnt mit dem charakteristischen Schlüsselwort, z.B. function
. Werden Module eingebunden (use ...
), so erfolgt dies vor jeder anderen Vereinbarung oder ausführbaren Anweisung. Danach folgt ein "Mischbereich" (im Fortran-Standard wird das natürlich nicht "Mischbereich" genannt). Vor dem end
-Statement darf ein contains
-Abschnitt mit Unterprogrammen stehen.
Fortran 90/95-Code (free source form) |
program (, function, subroutine, ...) ... use ... ! Hier folgt nun der so genannte "Mischbereich" contains interne Unterprogramme oder Modul-Unterprogramme end ... |
Im "Mischbereich" ist die Anordnung einzelner Elemente zwar auch im Standard geregelt, jedoch gibt es Elemente die an jeder Stelle des Mischbereichs auftreten dürfen, bei anderen ist wiederum klar festgelegt , dass sie nur nach oder vor anderen Elementen auftreten dürfen. Hier soll deshalb nur eine vereinfachte Variante gezeigt werden. Für genauere Informationen wird auf den Fortran-Working-Draft verwiesen.
Fortran 90/95-Code (free source form) |
program (, function, subroutine, ...) ... use ... ! --------- Beginn "Mischbereich" (mögliche Anordnungsreihenfolge) --------- implicit ... ! Definiton von Datenverbunden, Interface-Blöcke, benannte Konstanten, Variablendeklarationen ! ausführbare Anweisungen ... format ... ! --------- Ende "Mischbereich" -------------------------------------------- contains Interne Unterprogramme bzw. Modul-Unterprogramme end ... |
Nicht jede Anweisung ist in jeder Programmeinheit erlaubt. So dürfen z.B. keine format
-Anweisungen im Hauptteil eines Moduls (wohl aber in Modul-Unterprogrammen) verwendet werden.
Datentypen, Variablen, Wertzuweisungen, KonstantenBearbeiten
Dieses Kapitel handelt von Datentypen, Variablen, Konstanten und der Wertzuweisung.
DatentypenBearbeiten
Arithmetische DatentypenBearbeiten
Datentyp | Kommentar | Beispiele (Konstanten) |
---|---|---|
integer |
Ganzzahlen | 15, -6500, 200000000 |
real |
Gleitkommazahlen einfacher Genauigkeit | 3.1415, -5.5, .7e3, 12.5E-5 |
(double precision ) |
Gleitkommazahlen doppelter Genauigkeit (aus FORTRAN 77) | 3.1415D0, -5.5D0, .7d3, 12.5D-5 |
complex |
Komplexe Zahlen (zwei real -Zahlen) |
(3.1415, -5.5), (1.4, 7.1E4) |
double precision
ist in Fortran 95 nur noch aus historischen Gründen (FORTRAN 77) vorhanden. Fortran 95 bietet für Datentypen höherer Genauigkeit bzw. mit größerem Zahlenbereich andere Sprachmittel. Diese Thematik wird später erläutert (Datentypen höherer Genauigkeit).
Binär-, Oktal- oder Hexadezimalzahlen können als "boz literal constants" angegeben werden:
Schreibweise 1 | Schreibweise 2 | Kommentar |
---|---|---|
B"zahl" | B'zahl' | Binäre Zahl |
O"zahl" | O'zahl' | Oktalzahl |
Z"zahl" | Z'zahl' | Hexadezimalzahl |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: a a = Z"AB1C" write(*,*) a ! Ausgabe: 43804 a = O"7134" write(*,*) a ! Ausgabe: 3676 a = B'101110110001' write(*,*) a ! Ausgabe: 2993 end program bsp |
Logischer DatentypBearbeiten
Datentyp | Kommentar | Mögliche Werte |
---|---|---|
logical | Logischer Datentyp (wahr oder falsch) | .TRUE., .FALSE. |
ZeichenkettenBearbeiten
Datentyp | Kommentar | Beispiel (Konstante) |
---|---|---|
character(n) | Zeichenkette (String) mit einer Länge von n Zeichen | 'Hallo, Welt!', "Hallo, Welt!" |
character(len=n) | -"- | |
character*n | -"- (FORTRAN 77-Stil, sollte nicht mehr verwendet werden) | |
character | Zeichenkette (String) mit einer Länge von einem Zeichen | 'H', "h" |
Beachte: Zeichenketten können in Fortran 95 in Apostrophe oder Anführungszeichen eingeschlossen werden.
Beispiel:
"Wie geht's?"
'Er sagte: "Hallo"'
VariablenBearbeiten
Eine Variable ist charakterisiert durch einen
- symbolischen Namen
- Datentyp
- Wert
- Speicherplatz
Beim Programmstart hat eine Variable keinen definierten Wert. Eine Variable kann ihren Datentyp auf zwei Arten erhalten, durch implizite oder explizite Typanweisung.
Implizite TypanweisungBearbeiten
Bei der impliziten Typanweisung bestimmt der Anfangsbuchstabe des Variablenbezeichners den Datentyp.
Datentyp | Anfangsbuchstabe der Variablen |
---|---|
integer | Buchstaben I bis N (oder i bis n) |
real | restliche Buchstaben |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp b1 = 8.9 c1 = 3. i1 = B1/C1 write (*,*) i1 ! Das Ergebnis ist 2, da i1 implizit als integer definiert ist end program bsp |
Die Standardzuordnung der Anfangsbuchstaben kann durch das Schlüsselwort implicit
auch geändert werden. Infolge der durch implizite Typanweisung entstehenden Fehlermöglichkeiten ist es sinnvoll, die implizite Typanweisung komplett auszuschalten. Dies wird durch die Anweisung
implicit none
gleich nach der program
-Anweisung erreicht. Dann muss der Datentyp jeder Variablen explizit festgelegt werden.
Explizite TypanweisungBearbeiten
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none real :: b real :: c real :: i ! alternativ auch als real :: b, c, i b = 8.9 c = 3. i = b/c write (*,*) i ! Das Ergebnis ist 2.966666 end program bsp |
Benannte KonstantenBearbeiten
Benannte Konstanten werden in Fortran 95 folgendermaßen festgelegt:
datentyp, parameter :: symname = wert |
Beispiele:
real, parameter:: PI = 3.1415, PIFAC = PI/2.0
character(5), parameter :: str = 'Hallo'
Der zugewiesene Wert kann eine Konstante (Literal) oder eine schon definierte benannte Konstante sein.
Für die Zeichenkettenlänge ist bei benannten Konstanten auch eine *-Schreibweise möglich. Dies erspart die explizite Angabe der Stringlänge.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none character(*), parameter :: a = 'Jetzt wird auch die Stringlaenge festgelegt' write(*,*) a ! Ausgabe: Jetzt wird auch die Stringlaenge festgelegt end program bsp |
WertzuweisungBearbeiten
Wertzuweisungen haben wir schon kennengelernt:
variable = ausdruck |
Beispiel:
k = 1 k = k + 2
Die Wertzuweisung an eine Variable ist, wie am vorigen und auch am nächsten Beispiel zu ersehen, nicht zu verwechseln mit einer mathematischen Gleichung. Der Ausdruck
k + 2 = 5
wäre zwar mathematisch korrekt. Als Wertzuweisung in einem Fortran-Programm ist dies aber keine mögliche Formulierung. k+2
ist kein zulässiger Ausdruck auf der linken Seite des Zuweisungsoperators (L-Wert).
In Fortran ist auch keine Kette von Wertzuweisungen möglich. Der folgende Ausdruck ist nicht erlaubt und liefert eine Fehlermeldung.
i = j = k = 1.5 ! Fehler
FelderBearbeiten
BeispielBearbeiten
Fortran 90/95-Code (free source form) |
program bsp implicit none real, dimension(10) :: arr ! ACHTUNG! Array startet mit dem Index 1 ! arr(0) waere ein Fehler! arr(1) = 1.5 arr(2) = 2.5 arr(10) = 10.5 write(*,*) arr(1) ! 1.500000 wird ausgegeben write(*,*) arr(10) ! 10.500000 wird ausgegeben end program bsp |
Eindimensionale FelderBearbeiten
Statische SpeicherallokationBearbeiten
Variante 1: Die dimension
-AnweisungBearbeiten
real, dimension(10) :: arr
Der Feldindex läuft von 1 bis 10.
Variante 2: EinfachBearbeiten
real :: var(10)
Variante 3: Verwendung von benannten KonstantenBearbeiten
integer, parameter :: MAXIND = 10 real, dimension(MAXIND) :: arr
Hier erfolgt die Festlegung der Feldgröße über eine benannte Konstante.
Variante 4: Explizite Angabe der FeldgrenzenBearbeiten
real, dimension(0:9) :: arr
Hier wird Unter- und Obergrenze explizit angegeben. Der Index läuft nun von 0 bis 9. Die Feldgrenzen können auch negativ sein.
Dynamische SpeicherallokationBearbeiten
Mit Fortran 90/95 kann der benötigte Speicherplatz für ein Feld dynamisch angefordert werden. Zu diesem Zweck gibt es das Schlüsselwort allocatable
, welches unmittelbar bei der Felddeklaration die beabsichtigte dynamische Speicherallokation signalisiert.
datentyp, dimension(:), allocatable :: variablenbezeichner |
Die intrinsische Funktion allocate
dient dann der konkreten Speicherallokation im Aktionsteil des Programmes, also u.a. der Festlegung der konkreten Feldgröße. Der reservierte Speicherplatz kann mit der Funktion deallocate
auch wieder freigegeben werden.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none real, dimension(:), allocatable :: arr ! dynamisches eindimensionales Feld integer :: status ! Allokation allocate(arr(0:9), stat=status) ! Feld mit Werten belegen, ! .... ! .... ! Deallokation deallocate(arr, stat=status) end program bsp |
Die Statusvariable sollte nach Ausführung der Allokationsfunktionen jeweils den Wert 0 aufweisen. Andere Werte stehen für eine Fehlermeldung.
Die Funktion allocated()
hilft bei der Überprüfung des Allokationsstatus eines Feldes. Ist für das abgefragte Feld bereits Speicherplatz allokiert, dann liefert die Funktion .TRUE.
, ansonsten .FALSE.
! ... if (.not. allocated(arr)) then allocate(arr(groesse), stat=s) end if ! ...
Weitere Funktionen bezüglich Feldern sind im Kapitel Standardfunktionen aufgelistet.
Mehrdimensionale FelderBearbeiten
Für mehrdimensionale Felder gelten die gleichen Varianten wie für eindimensionale Felder. Die Speicherreihenfolge ist spaltenorientiert. Das bedeutet, der erste Index variiert am schnellsten:
Ein mehrdimensionales Feld kann auch in Fortran 90/95 maximal 7-dimensional gestaltet werden.
Beispiel: Ein 2-dimensionales FeldBearbeiten
Fortran 90/95-Code (free source form) |
program bsp implicit none character(10), dimension(0:9, 2:5) :: arr arr(0, 2) = 'Hallo' arr(1, 2) = 'Welt' ! ... arr(9, 5) = 'Universum' write (*,*) arr(0, 2) ! Ausgabe: Hallo write (*,*) arr(9, 5) ! Ausgabe: Universum end program bsp |
Beispiel: Spaltenorientierte SpeicherreihenfolgeBearbeiten
Die 3x3-Matrix soll in ein Fortran-Programm eingelesen und wieder komplett ausgegeben werden. Zusätzlich soll auch der Wert des Feldelementes a23 (2. Zeile, 3.Spalte, Wert=-2) separat ausgegeben werden.
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(3,3) :: arr(3,3) ! Feldelemente einlesen write (*,*) 'Werte (spaltenorientierte Eingabe):' read (*,*) arr ! Komplettes Feld ausgeben write (*,*) 'Gesamtfeld = ' , arr ! a23 ausgeben write (*,*) 'a23 = ', ARR(2,3) end program bsp |
Ein-/Ausgabe:
Werte (spaltenorientierte Eingabe): 1 40 -1 -5 3 9 0 -2 65 Gesamtfeld = 1 40 -1 -5 3 9 0 -2 65 a23 = -2
FeldinitialisierungBearbeiten
Felder lassen sich auch gleich bei der Deklaration mit Werten initialisieren.
Array constructorBearbeiten
Bei eindimensionalen Feldern kann die Feldinitialisierung direkt mittels array constructor erfolgen.
Beispiel:
real, dimension(5) :: arr = (/1.1, 1.2, 1.3, 2.1, 2.4/)
ReshapeBearbeiten
Für mehrdimensionale Felder besteht die Möglichkeit der direkten Verwendung des array constructors nicht.
Stattdessen kann eine derartige Initialisierung über den Umweg der reshape
-Funktion geschehen.
Beispiel: Die Matrix
7 8 9 7 8 9
soll in einem 2D-Feld gespeichert werden.
integer, dimension(2,3) :: arr = reshape( (/7, 7, 8, 8, 9, 9/), (/2, 3/) )
TeilfelderBearbeiten
Ähnlich wie das schon in FORTRAN 77 bei Zeichenketten möglich war, können nun auch Teilfelder direkt angesprochen werden.
Prinzip | Beschreibung |
---|---|
feldname (anfang:ende) | von anfang bis ende |
feldname (:ende) | vom 1. Feldelement bis ende |
feldname (anfang:) | von anfang bis zum letzten Feldelement |
|
Beispiel: Eindimensionale Felder
Fortran 90/95-Code (free source form) |
program bsp implicit none character(2), dimension(5) :: arr = (/'A1', 'A2', 'A3', 'A4', 'A5'/) integer, dimension(-3:1) :: i = (/-30, -20, -10, 0, 10/) write (*,*) arr(2:4) ! Ausgabe: A2A3A4 write (*,*) arr(2:) ! Ausgabe: A2A3A4A5 write (*,*) arr(:3) ! Ausgabe: A1A2A3 write (*,*) arr(1) ! Ausgabe: A1 write (*,*) arr ! Ausgabe: A1A2A3A4A5 write (*,*) i(-2:0) ! Ausgabe: -20 -10 0 end program bsp |
Arithmetische AusdrückeBearbeiten
Arithmetische OperatorenBearbeiten
Fortran 95 kennt wie FORTRAN 77 folgende arithmetische Operatoren
Operator | Kommentar |
---|---|
+ | Addition |
- | Subtraktion |
* | Multiplikation |
/ | Division |
** | Exponentiation |
OperatorenprioritätBearbeiten
Die Prioriät der arithmetischen Operatoren entspricht den mathematischen Konventionen.
- Klammerung vor allem anderen, z.B.
(a+b)*c
a*c+b*c
- Exponentiation vor Punktrechnung, z.B.
a*b**c
a*(b**c)
- Punktrechnung vor Strichrechnung, z.B.
a+b*c
a+(b*c)
Berechnungsfolge bei gleicher PrioritätBearbeiten
- Klammerung, Punktrechnung und Strichrechnung:
Beispiel:a*b/c*d
((a*b)/c)*d
- Exponentiation:
Beispiel:a**b**c
a**(b**c)
Außerdem ist zu beachten, dass niemals zwei Operatoren direkt aufeinander folgen dürfen.
Beispiel: Der Ausdruck 1.5**-1
ist in Fortran 95 falsch und führt zu einer Fehlermeldung. Richtig ist 1.5**(-1)
ErgebnisdatentypBearbeiten
Operanden gleichen DatentypsBearbeiten
Bei Operanden gleichen Datentyps erhält das Ergebnis den Datentyp der Operanden.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none real :: a a = 3/2 ! 3 ist ein integer und 2 ist auch ein integer, ! daher muss das Ergebnis auch ein integer sein, also 1. ! Die Zuweisung an die real-Variable a stellt das ! Ergebnis nicht mehr richtig. write(*,*) a ! Ausgabe: 1.00000 end program bsp |
Implizite Typumwandlung bei Operanden gemischten DatentypsBearbeiten
Weisen die Operanden unterschiedliche Datentypen auf, so wird bei jeder Operation, falls nötig, das Ergebnis dem höherwertigen Datentyp angepasst.
integer real complex |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none real :: a a = 3/2. ! 2. ist ein real. Jetzt stimmt das Ergebnis. write (*,*) a ! Ausgabe: 1.500000 end program bsp |
Explizite TypumwandlungBearbeiten
Fortran 95 besitzt auch Funktionen zur expliziten Umwandlung des Datentyps. Diese werden im Kapitel Standardfunktionen näher beschrieben.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none real :: r complex :: c r = 2. c = cmplx(r) write (*,*) c ! Ausgabe: ( 2.000000 , 0.000000 ) end program bsp |
Logische AusdrückeBearbeiten
Logische Ausdrücke können zwei Zustände annehmen, .TRUE.
oder .FALSE.
.
Logische OperatorenBearbeiten
Operator | Kommentar |
---|---|
.NOT. | logisches NICHT |
.AND. | logisches UND |
.OR. | logisches ODER |
.EQV. | logische Äquivalenz |
.NEQV. | logische Antivalenz |
WahrheitstafelBearbeiten
a | b | .NOT. a | a .AND. b | a .OR. b | a .EQV. b | a .NEQV. b |
---|---|---|---|---|---|---|
.TRUE. | .TRUE. | .FALSE. | .TRUE. | .TRUE. | .TRUE. | .FALSE. |
.TRUE. | .FALSE. | .FALSE. | .FALSE. | .TRUE. | .FALSE. | .TRUE. |
.FALSE. | .TRUE. | .TRUE. | .FALSE. | .TRUE. | .FALSE. | .TRUE. |
.FALSE. | .FALSE. | .TRUE. | .FALSE. | .FALSE. | .TRUE. | .FALSE. |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none logical :: l l = .TRUE. write(*,*) .NOT. l ! Ausgabe: F end program bsp |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none logical :: a, b a = .TRUE. b = .FALSE. write (*,*) a .NEQV. b ! Ausgabe: T end program bsp |
OperatorenprioritätBearbeiten
- Klammerung () bindet am stärksten
.NOT.
.AND.
.OR.
.EQV.
, bzw..NEQV.
VergleichsausdrückeBearbeiten
VergleichsoperatorenBearbeiten
Zum Vergleichen zweier arithmetischer Ausdrücke oder von Strings gibt es Vergleichsoperatoren. Das Ergebnis eines Vergleichs ist ein logischer Wert (.TRUE.
oder .FALSE.
).
Operator in Fortran 95 | Operator in FORTRAN 77 | Kommentar |
---|---|---|
< | .LT. | less than (kleiner als, <) |
<= | .LE. | less equal (kleiner gleich, <=) |
> | .GT. | greater than (größer als, >) |
>= | .GE. | greater equal (größer gleich, >=) |
== | .EQ. | equal (gleich, ==) |
/= | .NE. | not equal (ungleich, !=) |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: a, b a = 5 b = 6 write (*,*) A < B ! Ausgabe: T end program bsp |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none character(len=5) :: a, b a = "Halli" b = "Hallo" write (*,*) a < b ! Ausgabe: T end program bsp |
OperatorenprioritätBearbeiten
- Klammerung
- Arithmetische Operatoren
- Vergleichsoperatoren
- Logische Operatoren
StringoperationenBearbeiten
VerknüpfungsoperatorBearbeiten
Operator | Kommentar |
---|---|
// | Operator zum Verknüpfen von Strings |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none character(len=4) :: a character(len=10) :: b a = 'How ' b = 'do you do.' write(*,*) a // b ! Ausgabe: How do you do. end program bsp |
TeilkettenBearbeiten
Ein String ist ein character
-Feld. Auf die Stringelemente kann wie in einem Feld zugegriffen werden.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none character(10) :: a a='Hallo Welt' write(*,*) a(2:4) ! Ausgabe: all write(*,*) a(5:) ! Ausgabe: o Welt write (*,*) a(:3) ! Ausgabe: Hal end program bsp |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none character(10) :: a a='Hallo Welt' a(7:) = 'XYZ' write(*,*) a ! Ausgabe: Hallo XYZ end program bsp |
Verzweigungen und SchleifenBearbeiten
EinleitungBearbeiten
Neben der sequentiellen Programmausführung bietet Fortran 90/95 andere grundlegende Kontrollstrukturen zur Steuerung des Programmablaufs.
Verzweigungen | Schleifen | Terminierung |
---|---|---|
|
|
|
Zwecks komfortabler Manipulation von Feldinhalten stehen zusätzliche Spezialkonstrukte zur Verfügung:
|
All diese Programmelemente werden in diesem Kapitel detailliert dargestellt. Neben den genannten Steuerkonstrukten sind in Fortran 90/95 auch noch das berüchtigte goto
und einige als veraltet gekennzeichnete Sprunganweisungen möglich. Fortran 90 und Fortran 95 unterscheiden sich in Details bei Schleifen und Verzweigungen. Da Fortran 90 durch Fortran 95 bzw. beide durch den Fortran 2003-Standard abgelöst wurden, wird auf die Unterschiede nicht näher eingegangen.
if-VerzweigungenBearbeiten
Der if-EinzeilerBearbeiten
Form | Strukturbild | |
---|---|---|
|
Der if-Einzeiler oder das "if statement" ist die einfachste Form, um in Fortran-Programmen eine Verzweigung zu realisieren. Ist der angegebene logische Ausdruck wahr, dann wird die Anweisung ausgeführt, sonst nach dem if-Einzeiler mit der Programmausführung fortgesetzt. Dieses Konstrukt kann nur eine einzelne Anweisung verdauen!
Die gesamte if-Struktur inklusive Anweisung muss in einer Zeile angegeben werden. Eine konventionelle Zeilenfortsetzung mit dem &-Zeichen ist aber möglich. Einige Anweisungen sind nicht erlaubt. So kann die Anweisung nicht wieder ein "if statement" sein (geschachtelte if-Einzeiler). Ebenso darf der Anweisungsteil nicht aus end program
, end function
oder end subroutine
bestehen.
Beispiel:
program bsp
implicit none
integer :: i
i = 2
! Der logische Ausdruck ist .true. , "Hallo" wird ausgegeben
if (i == 2) print *, 'Hallo'
! Das funktioniert nicht und ist deshalb auskommentiert
! if (i == 2)
! print *, 'Hallo'
! So geht's aber
if (i == 2) &
print *, 'Hallo'
! Folgendes wiederum wird nicht das ev. erhoffte Resultat zeigen (= keine
! Ausgabe, da der logische Ausdruck .false.) Die durch Strichpunkt
! abgetrennte write-Anweisung gehört nicht mehr zum "if statement",
! sondern ist schon eine eigenständige Anweisung ausserhalb des
! if-statements
if (i == 3) print *, 'Hallo 1'; print *, 'Hallo 2'
! Ausgabe:
! Hallo
! Hallo
! Hallo 2
end program
if-thenBearbeiten
Kurzform | Langform | Strukturbild | ||
---|---|---|---|---|
|
|
Das if-then-Konstrukt erlaubt eine konventionelle einseitige Verzweigung. Im Gegensatz zum if-Einzeiler ist hier ein Anweisungsblock mit beliebig vielen Anweisungen erlaubt. Einem if-then-Konstrukt, wie auch den meisten nachfolgenden Verzweigungs- und Schleifentypen, kann eine Bezeichnung mitgegeben werden.
Beispiel:
program bsp
implicit none
integer :: i
i = 2
if (i == 2) then
print *, 'Hallo1'
end if
CHECKIT: if (i /= 3) then
print *, 'Hallo2'
exit CHECKIT
print *, 'Hallo3' ! <- wird nicht mehr ausgefuehrt
end if CHECKIT
! Ausgabe:
! Hallo1
! Hallo2
end program
if-then-elseBearbeiten
Dies ist eine typische zweiseitige Verzweigung. Je nachdem, ob die Bedingung zu wahr oder falsch ausgewertet wird, wird der if- oder der else-Anweisungsblock ausgeführt, um nach dem Verzweigungsende wieder den gleichen Programmcode abzuarbeiten. Bei der Langform (benanntes if-then-else-Konstrukt) ist übrigens auch noch eine andere Variante möglich. So könnte der Bezeichner name
nach else
auch weggelassen werden. Nach dem end if
ist er, sofern die benannte Form gewählt wurde, aber auf jeden Fall anzugeben.
Beispiel:
program bsp
implicit none
real :: t, tc, tr
character :: conv
print *, "Gib Temperatur in Kelvin ein:"
read (*,*) t
CHECKRANGE: if (t >= 0.0) then
print *, "Umrechnung (c -> in Celsius, anderes Zeichen -> in Rankine):"
read (*,*) conv
if (conv == "c") then
tc = t - 273.15
print *, tc
else
tr = 1.8 * t
print *, tr
end if
else CHECKRANGE
print *, "Fehler: t < 0.0 K"
end if CHECKRANGE
end program
else-ifBearbeiten
Kurzform | Langform | ||
---|---|---|---|
|
| ||
Strukturbild |
Das ist die allgemeinste Form der if-Verzweigung und wird im Fortran-Standard als "if construct" bezeichnet. Die vorher beschriebenen Formen sind nur vereinfachte Spezialfälle dieses Verzweigungstyps.
Beispiel:
program bsp
implicit none
integer :: i
print *, "Gib eine natuerliche Zahl ein:"
read (*,*) i
! *** Langform ***
I999: if (i == 1) then
print *, "A"
else if ((i > 1) .and. (i <= 5)) then I999
print *, "BCDE"
else if ((i > 5) .and. (i <= 11)) then I999
print *, "FGHIJK"
else I999
print *, "L-Z"
end if I999
! *** Das Gleiche in Kurzform ***
if (i == 1) then
print *, "A"
else if ((i > 1) .and. (i <= 5)) then
print *, "BCDE"
else if ((i > 5) .and. (i <= 11)) then
print *, "FGHIJK"
else
print *, "L-Z"
end if
end program
Die select case-VerzweigungBearbeiten
Kurzform | Langform | Strukturbild | ||
---|---|---|---|---|
|
|
Eine andere Möglichkeit zur Erstellung von Verzweigungen stellt die "select case"-Steueranweisung (das "case construct") bereit. Die variable
kann vom Typ integer
, logical
oder character
sein. Die Fälle werden durch Konstanten repräsentiert. Es muss nicht jeder Fall einzeln angeschrieben werden. Fälle, die zwar die gleichen Aktionen ausführen sollen, aber durch verschiedene Konstanten dargestellt werden, können zu einem Fall zusammengezogen werden. Optional kann auch noch ein "Default"-Fall angegeben werden.
Möglichkeiten um Fälle festzulegen:
n | Einzelne Konstante | gleich n |
n1, n2, n3 | Fallliste | n1 oder n2 oder n3 |
n: | Bereich | von n bis ... |
:m | Bereich | von ... bis m |
n:m | Bereich | von n bis m |
Die in der Tabelle gelisteten Alternativen können natürlich auch kombiniert werden, z.B. eine Liste aus Einzelkonstanten und Bereichen. Bei logischem Datentyp ist die Angabe von Bereichen natürlich unsinnig.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: i read( *, * ) i select case( i ) case( :111 ) write( *, * ) 'Fall 1' case( 112:332, 334 ) write( *, * ) 'Fall 2' case( 333 ) write( *, * ) 'Fall 3' case default write( *, * ) 'unspezifiziertes Ereignis' end select ! Ausgabe (Eingabe: 222): ! Fall 2 ! Ausgabe (Eingabe: -5): ! Fall 1 ! Ausgabe (Eingabe: 333): ! Fall 3 ! Ausgabe (Eingabe: 5000): ! unspezifiziertes Ereignis end program bsp |
do-SchleifenBearbeiten
Fortran kennt nur einen allgemeinen Schleifentyp – die do-Schleife. Diese do-Schleife gibt es aber in verschiedenen Ausprägungen, so dass damit ziemlich alle denkbaren Einsatzfälle abgedeckt sind.
Im Prinzip haben alle do-Schleifen die Form
do [...] ... end do |
do-if-exitBearbeiten
Kurzform | Langform | Strukturbild | ||
---|---|---|---|---|
|
|
Lässt man den anweisungsblock1
weg, dann erhält man eine kopfgesteuerte Schleife. Entfällt der anweisungsblock2
, so ist die Schleife fußgesteuert. Ohne Abbruchbedingung ist dies eine Endlosschleife.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: i = 1 do write( *, * ) i i = i + 1 if( i > 10 ) exit end do write( * , * ) "Die do-Schleife wurde beendet" ! Ausgabe ! 1 ! 2 ! ... ! 10 ! Die do-Schleife wurde beendet end program bsp |
Die do-ZählschleifeBearbeiten
Kurzform | Langform | ||
---|---|---|---|
|
| ||
Strukturbild |
Zählvariable, Startwert, Endwert und Schrittweite müssen vom Typ integer
sein. Die Zählvariable darf in der Schleife nicht manipuliert werden. Wird die Schrittweite nicht explizit vorgegeben, so hat sie den Wert 1.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: i do i = 1, 10 write( *, * ) i end do ! Zeilenweise Ausgabe der Zahlen 1 bis 10 end program bsp |
do whileBearbeiten
Kurzform | Langform | Strukturbild | ||
---|---|---|---|---|
|
|
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: i i = 0 do while( i < 5 ) write( *, * ) i i = i + 1 end do ! Die Zahlen 0 bis 4 werden ausgegeben end program bsp |
Die implizite do-ListeBearbeiten
Bei Eingabe oder Ausgabe ist die Angabe einer impliziten do-Liste möglich.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: i write( *, * ) ( 'Hallo', i = 1, 10 ) ! Ausgabe: HalloHalloHalloHalloHalloHalloHalloHalloHalloHallo end program bsp |
Spezialkonstrukte für FelderBearbeiten
whereBearbeiten
Sollen in Feldern Elemente manipuliert werden, so können die where
-Anweisungen (das "masked array assignment") hilfreich sein. Dabei lassen sich Feldelementen in Abhängigkeit von vorgegebenen Bedingungen (Maske) neue Werte zuweisen.
Der where-EinzeilerBearbeiten
Form | |
---|---|
|
Das "where statement" muss komplett in eine Zeile geschrieben werden, wobei Zeilenumbrüche mit dem &-Zeilenumbruchsmarker jedoch erlaubt sind. Für komplexere Probleme ist das im nächsten Abschnitt vorgestellte "where construct" (where-elsewhere) vorgesehen.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(5) :: arr = (/ 5, 1, -1, 1, 7 /) write( *, * ) arr ! Ausgabe: 5 1 -1 1 7 where( arr >= 3 ) arr = 99999 write(*,*) arr ! Ausgabe: 99999 1 -1 1 99999 end program bsp |
where-elsewhereBearbeiten
Kurzform | Langform | ||
---|---|---|---|
|
|
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(10) :: arr = (/ 5, 1, -1, 1, 7, -3, 7, 6, -9, 8 /) where( arr <= 0 ) arr = 0 elsewhere( arr > 0 .and. arr <= 3 ) arr = 1 elsewhere arr = 2 end where write(*,*) arr ! Ausgabe: 2 1 0 1 2 0 2 2 0 2 end program bsp |
forallBearbeiten
Auch die forall
-Schleife ist für den Einsatz bei Feldern gedacht. Im Gegensatz zum "where construct" werden hier die ausgewählten Feldelemente in erster Linie über die Indizes und erst in zweiter Linie über den Wert bestimmt.
Der forall-EinzeilerBearbeiten
Formen | |
---|---|
| |
|
Die erste angegebene Form ist für eindimensionale Felder gedacht, die zweite für mehrdimensionale Felder. Über subscript
kann der Indexbereich festgelegt werden. Das optionale stride
gibt die Schrittweite vor. Wird dieser Wert nicht angegeben, so ist die Schrittweite = 1.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(5) :: a = (/ 1, 2, 3, 4, 5 /) integer, dimension(2, 2) :: b = reshape( (/ 0, 2, -1, 5 /), (/ 2, 2 /) ) integer :: i, j forall( i = 1:3 ) a(i) = 0 write( *, * ) a ! Ausgabe: 0 0 0 4 5 forall( i = 1:5:2 ) a(i) = -1 write( *, * ) a ! Ausgabe: -1 0 -1 4 -1 forall( i = 1:5 ) a(i) = max ( a(i), 3 ) write( *, * ) a ! Ausgabe: 3 3 3 4 3 forall( i = 1:2, j = 2:2 ) b(i, j) = -9 write( *, * ) b ! Ausgabe: 0 2 -9 -9 end program bsp |
Aber das ist nicht alles, was der forall-Einzeiler zustande bringt. Zusätzlich zur Feldelementbestimmung über Indexbereiche kann auch eine Maske vorgegeben werden. Die forall-Schleife ist also auch eine erweiterte where-Anweisung.
Form | |
---|---|
|
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(5) :: a = (/ 1, 2, 3, 4, 5 /) integer :: i, j forall( i = 1:4, a(i) > 2 ) a(i) = 0 write( *, * ) a ! Ausgabe: 1 2 0 0 5 end program bsp |
Bei der Verwendung von forall-Anweisungen sind einige Prämissen zu beachten. So müssen nebst anderem in Masken oder im Anweisungsbereich verwendete Funktionen pure
sein. Was das genau bedeutet wird später im Kapitel Unterprogramme erläutert
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(5) :: a = (/ 1, 2, 3, 4, 5 /) integer :: i ! Die Funktion berechne_1() darf nicht innerhalb einer forall-Anweisung ! aufgerufen werden, da sie nicht "pure" ist ! forall( i = 1:4, a(i) > 2 ) a(i) = berechne_1( a(i) ) ! FEHLERMELDUNG ! Die Funktion berechne_2 macht das Gleiche wie berechne_1(), ! ist aber als "pure" gekennzeichnet und darf in der forall-Anweisung ! aufgerufen werden forall( i = 1:4, a(i) > 2 ) a(i) = berechne_2( a(i) ) ! OK write( *, * ) a ! Ausgabe: 1 2 9 12 5 contains integer function berechne_1( val ) integer, intent( in ) :: val integer :: res res = val * 3 berechne_1 = res end function berechne_1 pure integer function berechne_2( val ) integer, intent( in ) :: val integer :: res res = val * 3 berechne_2 = res end function berechne_2 end program bsp |
Das "forall construct"Bearbeiten
Kurzform |
| |
---|---|---|
Langform |
|
Es gilt im Prinzip das gleiche wie für den forall-Einzeiler. Es sind innerhalb der Schleife eben mehrere Anweisungen erlaubt. Es gelten auch die Formen für mehrdimensionale Felder und mit Vorgabe einer Maske. Diese sind in diesem Abschnitt aber nicht mehr explizit angeführt.
TerminierungBearbeiten
stopBearbeiten
Die stop
-Anweisung beendet das Programm.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none write( *, * ) 'Vor Stop-Statement' stop write( *, * ) 'Nach Stop-Statement' ! Ausgabe: ! Vor Stop-Statement end program bsp |
Der stop-Anweisung kann auch ein "stop-code" mitgegeben werden. Dieser "stop-code" kann eine Ganzzahl (maximal 5-stellig) oder eine Zeichenkette sein. Der "stop-code" wird bei der Programmtermination ausgegeben. Wo die Ausgabe erfolgt und wie sie aussieht ist aber betriebssystem- und compilerabhängig.
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: i read( *, * ) i if( i == 1 ) then stop 999 else stop "Sonstiger Stop" end if ! Ausgabe (bei Eingabe von 1): ! ifort (Linux): ! 999 ! gfortran, g95 (Linux): ! STOP 999 ! Sun f95 (Linux): ! STOP: 999 end program bsp |
exitBearbeiten
Mit der Anweisung exit
kann eine do-Schleife verlassen werden. Wird eine do-Schleife mit einer Bezeichnung versehen, so kann bei der exit
-Anweisung explizit auf die benannte Schleife Bezug genommen werden. Wird kein Schleifenname angegeben, so bezieht sich die exit
-Anweisung immer auf die innerste Schleife, mit der sie im Zusammenhang steht.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: i, j outerloop: do i = 1, 5 write( *, * ) "Outer", i, "Beginn" innerloop: do j = 1, 3 if( i == 1 ) exit if( i == 2 ) exit innerloop if( i == 4 ) exit outerloop write( *, * ) "Inner", j end do innerloop write( *, * ) "Outer", i, "Ende" end do outerloop ! Ausgabe: ! Outer 1 Beginn ! Outer 1 Ende ! Outer 2 Beginn ! Outer 2 Ende ! Outer 3 Beginn ! Inner 1 ! Inner 2 ! Inner 3 ! Outer 3 Ende ! Outer 4 Beginn end program bsp |
cycleBearbeiten
Mit cycle
wird der aktuelle do-Schleifendurchlauf beendet und wieder zum Schleifenkopf gesprungen. Wird eine do-Schleife mit einer Bezeichnung versehen, so kann bei der cycle
-Anweisung explizit auf die benannte Schleife Bezug genommen werden. Wird kein Schleifenname angegeben, so bezieht sich die cycle
-Anweisung immer auf die innerste Schleife, mit der sie im Zusammenhang steht.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: i, j outerloop: do i = 1, 5 write( *, * ) "Outer", i, "Beginn" innerloop: do j = 1, 3 if( i == 1 ) cycle outerloop if( i == 4 ) cycle innerloop if( i == 5 ) cycle write( *, * ) "Inner", j end do innerloop write( *, * ) "Outer", i, "Ende" end do outerloop ! Ausgabe: ! Outer 1 Beginn ! Outer 2 Beginn ! Inner 1 ! Inner 2 ! Inner 3 ! Outer 2 Ende ! Outer 3 Beginn ! Inner 1 ! Inner 2 ! Inner 3 ! Outer 3 Ende ! Outer 4 Beginn ! Outer 4 Ende ! Outer 5 Beginn ! Outer 5 Ende end program bsp |
Datentypen höherer GenauigkeitBearbeiten
Fortran 95 bietet für die Festlegung von Datentypen höherer Genauigkeit einen neuen Ansatz.
Einfache VarianteBearbeiten
Mittels kind
(dt.: Art, Sorte, Gattung, Wesen) kann die Genauigkeit bzw. der Wertebereich eines Datentyps festgelegt werden. Die einfache Variante ist aber system- und compilerabhängig.
datentyp (kind=wert) :: var |
Kurzform:
datentyp (wert) :: var |
Für wert
ist meist (aber nicht immer) die Anzahl der Bytes einzusetzen. Literale eines solchen Datentyps sind mit einem entsprechenden Anhängsel zu kennzeichnen, z.B.:
- 3.141592654_8
- -4.9e55_8
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none real(kind=8) :: variable ! variable ist nun vom Datentyp "double precision" variable = 1.55555555555_8 write(*,*) variable ! Ausgabe: 1.55555555555000 ! Hier wird nur eine gewöhnliche real-Zahl mit 7 Nachkommastellen ! zugewiesen variable = 1.55555555555 write(*,*) variable ! Ausgabe: 1.55555558204651 end program bsp |
System- und compilerunabhängige VarianteBearbeiten
Das kind
-Attribut darf über eine Konstante oder eine benannte Konstante belegt werden. So kann statt
real(kind=8) :: var
auch
integer, parameter :: dp = 8 real(kind=dp) :: var
geschrieben werden. Der entsprechende kind
-Wert ist auch per Funktion ermittelbar. Die Variable var
integer, parameter :: dp = kind(0.0d0) real(kind=dp) :: var
ist in diesem Beispiel somit vom Datentyp double precision
. Das ist schon compilerunabhängig verwendbarer Fortran-Code.
Nachfolgend wird die selected_real_kind(p, r)
-Funktion vorgestellt, die durch Vorgabe der gewünschten Nachkommastellen und/oder des Exponentenmaximalwertes einen potentiell geeigneten kind
-Wert für Gleitkommazahlen ermittelt.
selected_real_kindBearbeiten
integer, parameter :: name = selected_real_kind(anzahl_nachkommastellen, max_exponentenwert) |
Die Funktion selected_real_kind
gibt einen Wert zurück, der alle Gleitkommazahlen mit mindestens anzahl_nachkommastellen
Dezimalstellen Genauigkeit und einem Exponentenbereich von max_exponentenwert
berücksichtigt. Gibt es keinen solchen systemspezifischen Wert, so wird einer der folgenden Werte zurückgegeben:
- -1 ... die geforderte Anzahl an Dezimalstellen ist nicht verfügbar
- -2 ... der Exponentenbereich ist nicht verfügbar
- -3 ... nichts von beidem ist verfügbar
Wird eine solche Konstante mit negativem Wert nachfolgend als kind-Wert bei der Deklaration einer Variablen, Konstanten etc. verwendet, so führt dies in aller Regel zu einer Fehlermeldung bereits beim Kompiliervorgang.
Es ist auch möglich die selected_real_kind
-Funktion mit nur einem Argument aufzurufen. Die Argumente sind mit p
(für precision) bzw. r
(für range) benannt.
Beispiele:
integer, parameter :: dp = selected_real_kind(r=60)
integer, parameter :: dp = selected_real_kind(p=15)
integer, parameter :: ultrap = selected_real_kind(1000, 5000) ! liefert höchstwahrscheinlich ! die negative Zahl -3
VariablendeklarationBearbeiten
real (kind=name) :: variable |
Kurzform:
real (name) :: variable |
Beispiel:
integer, parameter :: dp = selected_real_kind(r=60) real(kind=dp) :: var
KonstantenBearbeiten
zahl_name |
Literale eines solchen Datentyps sind mit dem im Programmcode festgelegten charakteristischen Anhängsel zu kennzeichnen, z.B.:
- 3.141592654_dp
- -4.9e55_dp
BeispielBearbeiten
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, parameter :: dp = selected_real_kind(15, 300) real(kind=dp) :: variable variable = 1.5555555555_dp / 2_dp write(*,*) variable ! Ausgabe: 0.777777777750000 write(*,*) kind(variable) ! Ausgabe: 8 end program bsp |
DatenverbundBearbeiten
EinleitungBearbeiten
Datenelemente lassen sich auch zu eigenen zusammengesetzten Datentypen verbinden. Der Datenverbund ist in anderen Programmiersprachen auch als Struktur (structure, struct) bekannt. Der Fortran 95-Standard spricht vom "derived type", also einem abgeleiteten Datentyp. Zulässig als Komponenten eines Datenverbundes sind Skalare, Felder, Zeiger oder auch andere Datenverbunde. Ein Datenverbund ist durch das Schlüsselwort type
gekennzeichnet und kann in folgenden Formen auftreten
|
|
Die Elemente in eckigen Klammern sind optional. Die Angabe eines Zugriffsspezifizierers (public
, private
) ist nur bei Verwendung von Datenverbunden in Modulen erlaubt.
Einen Datenverbund anlegenBearbeiten
type :: name [sequence] ! optional sequentielle Speicherplatzablage datentyp :: komponentenname_1 ! ... datentyp :: komponentenname_n end type [name] |
Beispiel (Programmausschnitt):
Fortran 90/95-Code (free source form) |
! ... type :: koord integer :: id real, dimension(3) :: x end type koord ! ... |
Eine Variable vom Typ "Datenverbund" anlegenBearbeiten
Die Variablendeklaration erfolgt in der Form
type( name ) :: var |
Beispiel (Programmausschnitt):
Fortran 90/95-Code (free source form) |
! ... type :: koord integer :: id real, dimension(3) :: x end type koord type( koord ) :: k ! ... |
Zugriff auf Einzelkomponenten eines DatenverbundsBearbeiten
Eine Einzelkomponente in einem Datenverbund lässt sich als Kombination des Datenverbundvariablenbezeichners und des Komponentennamen ansprechen. Verknüpft werden diese beiden Bezeichner mit dem %
-Zeichen.
var%komponentenname |
Beispiel (Programmausschnitt):
Fortran 90/95-Code (free source form) |
! ... type :: koord integer :: id real, dimension(3) :: x end type koord type( koord ) :: k k%id = 999 k%x(1) = 0.0 k%x(2) = 20.5 k%x(3) = 10.0 write( *, * ) k%x(2) ! ... |
Zugriff auf einen Datenverbund als GanzesBearbeiten
Ein Datenverbund kann in bestimmten Situation nicht nur komponentenweise angesprochen werden, sondern auch als Einheit, z.B. bei der Ein-/Ausgabe oder beim Zuweisen der Werte einer Datenverbundvariablen an eine andere.
Beispiel (Programmausschnitt):
Fortran 90/95-Code (free source form) |
! ... type :: koord integer :: id real, dimension(3) :: x end type koord type( koord ) :: k1, k2 read( *, * ) k1 k2 = k1 write( *, * ) k2 ! ... |
Elemente eines Datenverbunds initialisierenBearbeiten
Default-Werte im Datenverbund setzenBearbeiten
Innerhalb eines Datenverbundes lassen sich die einzelnen Variablen mit Vorgabewerten belegen, z.B.
integer :: i = 5 character, dimension( 2, 2 ) :: c = reshape( (/ "A", "B", "C", "D" /), (/ 2, 2 /) )
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none type :: koord integer :: id = -999999 real, dimension( 3 ) :: x = 0.0 end type koord type( koord ) :: k write( *, * ) k ! Ausgabe: ! -999999 0.0 0.0 0.0 end program bsp |
Der "structure constructor"Bearbeiten
Die Initialisierung der Datenverbundelemente bzw. eine spätere Wertzuweisung kann auch per "structure constructor" ("derived type value constructor") vorgenommen werden.
name( wertliste ) |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none type :: koord integer :: id real, dimension( 3 ) :: x end type koord type( koord ) :: k = koord( 12, (/ 0.0, 1.5, 20.5 /) ) write( *, * ) k k = koord( 13, (/ 5.5, 0.0, 0.5 /) ) write( *, * ) k ! Ausgabe: ! 12 0.0 1.5 20.5 ! 13 5.5 0.0 0.5 end program bsp |
Enthält ein Datenverbund bereits Variablen mit Vorgabewerten, so werden diese durch die Werte im "structure constructor" überschrieben.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none type :: person character(25) :: vorname = 'NN' character(25) :: nachname = 'NN' integer :: alter end type person type(person), dimension(5) :: p p(1) = person('Harri', 'P.', 41) p(5) = person('Creszenzia', 'T.', 18) write(*, fmt=111) p ! Ausgabe: ! Harri P. 41 ! NN NN 0 ! NN NN 0 ! NN NN 0 ! Creszenzia T. 18 write(*,*) p(1)%vorname ! Ausgabe: ! Harri p(2)%vorname = 'Holger' write(*, '(A)') p%vorname ! Ausgabe: ! Harri ! Holger ! NN ! NN ! Creszenzia 111 format(2A, I3) end program bsp |
Beispiel: Verwendung eines Datenverbunds in einem anderen Datenverbund
Fortran 90/95-Code (free source form) |
program bsp implicit none type :: tripel real :: x, y, z end type tripel type :: koerper integer :: id type( tripel ) :: bezugskoordinate type( tripel ) :: orientierung end type koerper type( koerper ) :: k k = koerper( 1005, tripel( 10.5, 0.0, -6.5 ), tripel( 0.0, 0.0, -0.8 ) ) write( *, * ) k ! Ausgabe: ! 1005 10.50000 0.000000 -6.500000 0.000000 ! 0.000000 -0.8000000 end program bsp |
Das Attribut sequence
Bearbeiten
Im Datenverbund kann zu Beginn auch das Attribut sequence
angegeben werden. Dieses spezifiziert, dass die Werte eines Datenverbunds sequentiell gespeichert werden. Wird dieses Attribut weggelassen, so ist nicht gewährleistet, dass die Werte von Datenverbundvariablen in der selben Reihenfolge, wie sie im Datenverbund gereiht wurden im Speicher wiederzufinden sind, oder dass sie zusammenhängend gespeichert werden. Oft wird die Speicherreihenfolge keine wesentliche Rolle spielen. Ein Spezialfall, wo die Speicherreihenfolge jedoch essenziell ist, wird anschließend dargestellt.
Die Verwendung eines Datenverbunds in separaten ProgrammeinheitenBearbeiten
Ein Datenverbund kann über ein Modul verschiedenen Programmeinheiten bekannt gemacht werden. Dazu aber später. Es ist nämlich auch so möglich in komplett getrennten Programmeinheiten auf ein und denselben Datenverbund-Datentyp zuzugreifen.
Beispiel: Ein ähnliches Beispiel findet sich auch im J3/97-007R2 Working Draft, Note 4.31
Fortran 90/95-Code (free source form) |
program bsp implicit none type :: koord sequence integer :: id real, dimension( 3 ) :: x end type koord type( koord ) :: k = koord( 555, (/ 3.0, 4.0, 5.5 /) ) call ausgabe( k ) ! Ausgabe ! 555 3.0000000 4.0000000 5.5000000 end program bsp subroutine ausgabe( coord ) type :: koord sequence integer :: id real, dimension( 3 ) :: x end type koord type( koord ), intent( in ) :: coord write( *, * ) coord end subroutine ausgabe |
In diesem speziellen Fall muss der Datenverbund lt. Standard mit dem Attribut sequence
versehen werden. Des Weiteren muss der im Hauptprogramm deklarierte Datenverbund komplett identisch mit jenem im Unterprogramm sein (Datenverbundname, Variablenbezeichner, Reihenfolge, Datentypen). In der Praxis sehen das viele Compiler nicht so eng. Allerdings können bei Nichtbeachtung dieser Vorgaben spätestens beim Wechsel des Compilers oder auf andere Rechnerarchitekturen gröbere Probleme auftreten.
StandardfunktionenBearbeiten
Allgemeine HinweiseBearbeiten
Die in den nachfolgenden Abschnitten angeführten Tabellen und Listen geben die im Fortran 95-Working Draft gelisteten intrinsischen Funktionen in simplifizierter Form wieder. Einzelheiten stehen jeweils in Unterkapiteln.
Auch die Standard-Subroutinen wurden berücksichtigt. Die einzelnen Compiler kennen zum Teil wesentlich mehr Funktionen als im Standard vorgegeben. Bei Verwendung solcher Funktionen sind die Programme jedoch nicht mehr quellcodekompatibel. Auf die Wiedergabe solcher compilerspezifischen Funktionen wird hier deshalb verzichtet.
TabellenlegendeBearbeiten
Abkürzung | Beschreibung |
---|---|
i | Integer-Datentyp (integer) |
r | Real-Datentyp (real) |
x | Complex-Datentyp (complex) |
d | Double-precision-Datentyp (real(z, kind(0.0D0))) |
z | beliebiger numerischer Datentyp (integer, real, complex) |
c | Zeichen (character) |
l | Logical-Datentyp (logical) |
any | beliebiger intrinsischer Datentyp |
arr | Feld (Array) |
aarr | dynamisches Feld |
ptr | Zeiger (Pointer) |
DatentypfunktionenBearbeiten
- Umwandlung in INTEGER
- Umwandlung in REAL
- Umwandlung in DOUBLE PRECISION
- Umwandlung in COMPLEX
- Umwandlung in CHARACTER
- kind-Parameter
Einzelheiten siehe Datentypfunktionen
Mathematische FunktionenBearbeiten
- Rundung
- Absolutwert
- Modulo
- Vorzeichentransfer
- Positive Differenz
- Maximum
- Minimum
- Komplexe Zahlen
- Quadratwurzel
- Exponentialfunktion
- Logarithmen
- Winkelfunktionen
- Arkusfunktionen
- Hyperbelfunktionen
Einzelheiten siehe Mathematische Funktionen
StringfunktionenBearbeiten
- Lexikalische Funktionen
- Sonstige
Einzelheiten siehe Stringfunktionen
FeldfunktionenBearbeiten
- Konstruktion und Umgestaltung von Feldern
- Abfragen von Feldstatus, Felddaten und Feldmetadaten
- Funktionen für Vektoren und Matrizen
- Sonstige
Einzelheiten siehe Feldfunktionen
ZeigerfunktionenBearbeiten
Einzelheiten siehe Zeigerfunktionen
BitfunktionenBearbeiten
Einzelheiten siehe Bitfunktionen
Weitere FunktionenBearbeiten
Einzelheiten siehe Weitere Funktionen
Intrinsische SubroutinenBearbeiten
Einzelheiten siehe Intrinsische Subroutinen
UnterprogrammeBearbeiten
Werden Programme umfangreicher und komplexer oder werden einzelne Programmabschnitte öfter verwendet, dann ist es sinnvoll Unterprogramme (Prozeduren) zu verwenden. Fortran 95 kennt zwei Typen von Unterprogrammen:
- Funktionen (
function
) - Subroutinen (
subroutine
)
Darüber hinaus existiert in Fortran die Möglichkeit Unterprogramme und Daten mittels Modulen in das Programm einzubeziehen.
Das function
UnterprogrammBearbeiten
Eine Funktion ist ein Unterprogramm, das genau einen Wert zurück gibt. Welcher Art von Datentyp der Wert ist, wird durch eine Zuweisung an den Funktionsnamen erreicht ( z. B. real
).
Die allgemeine Funktionsdeklaration lautet:
datentyp function funktionsname ( [formale parameter] ) Vereinbarungsteil Anweisungsteil funktionsname = wert [return] end function funktionsname |
Wird der Datentyp bei der Funktionsdeklaration nicht angegeben, so muss der Datentyp der Funktion im Vereinbarungsteil festgelegt werden:
function funktionsname ( [formale parameter] ) Vereinbarungsteil datentyp :: funktionsname Anweisungsteil funktionsname = wert [return] end function funktionsname |
Mittels return
kann zur aufrufenden Programmeinheit zurückgekehrt werden. Ein return
unmittelbar vor der end
-Anweisung ist nicht unbedingt erforderlich, da das Programm beim Erreichen der end
-Anweisung zur aufrufenden Programmeinheit zurück springt. Deswegen sollte bei allen neueren Programmen darauf verzichtet werden. Wichtig ist die return
-Anweisung für alternative Rücksprünge zur aufrufenden Programmeinheit.
Eine Funktion wird meist mittels folgender allgemeiner Anweisung von der ausführenden Programmeinheit aufgerufen:
variable = funktionsname( aktuelle parameter ) |
Dabei orientiert sich diese Unterprogrammtechnik direkt an mathematischen Funktionen. Im folgenden Beispiel ruft das Programm test
die Funktion funk
auf, führt das Unterprogramm aus und gibt den entsprechenden Rückgabewert an das Programm test
zurück:
Datei bsp.f95:
Fortran 90/95-Code (free source form) |
program test ! implicit none real :: funk real :: x, y write(*,*) 'x -> ' read(*,*) x y = funk( x ) write(*,*) 'funk -> ', y end program test real function funk( a ) ! implicit none real :: a funk = a**2 end function funk |
Das Unterprogramm funk
kann sich direkt unter der aufrufenden Programmeinheit in der selben Textdatei oder in einer separaten Datei befinden. Befindet sich das Unterprogramm in einer separaten Datei, so muss diese und die aufrufende Programmeinheit zusammen kompiliert werden:
Übersetzung mit gfortran im vorliegenden Fall:
gfortran -o bsp bsp.f95
Übersetzung mit gfortran bei separaten Dateien (bspw.):
gfortran -o bsp bsp.f95 funk.f95
Es ist aber auch möglich eine Funktion nur durch ihren Namen aufzurufen. Der Rückgabewert der Funktion wird dann direkt ausgegeben:
Datei bsp.f95:
Fortran 90/95-Code (free source form) |
program test ! implicit none real :: funk real :: x write(*,*) 'x -> ' read(*,*) x write(*,*) 'funk -> ', funk( x ) end program test real function funk( a ) ! implicit none real :: a funk = a**2 end function funk |
Das subroutine
UnterprogrammBearbeiten
Eine subroutine
ist ein Unterprogramm, das mehr als einen Wert zurückgeben kann. Eine Subroutine besitzt im Gegensatz zu einer Funktion keinen Datentyp und Rückgabewert.
Die allgemeine Deklaration einer Subroutine lautet:
subroutine subroutinenname ([formale parameter]) Vereinbarungsteil Anweisungsteil [return] end subroutine subroutinenname |
Mittels return
kann zur aufrufenden Programmeinheit zurückgekehrt werden. Ein return
unmittelbar vor der end
-Anweisung ist nicht unbedingt erforderlich, da das Programm beim Erreichen der end
zur aufrufendenen Programmeinheit zurück springt. Deswegen sollte bei allen neueren Programmen darauf verzichtet werden. Wichtig ist die return
Anweisung für alternative Rücksprünge zur aufrufenden Programmeinheit.
Aufgerufen wird eine Subroutine aus der aufrufenden Programmeinheit mittels der call
-Anweisung:
call subroutinenname [([aktuelle parameter])] |
Im folgenden Beispiel wird die Subroutine sub
aufgerufen. Diese gibt dann zwei Werte zurück:
Datei bsp.f95
Fortran 90/95-Code (free source form) |
program bsp ! implicit none real :: x, y, z write( *, * ) 'x -> ' read( *, * ) x call sub( x, y, z ) write( *, * ) 'y -> ', y, 'z -> ', z end subroutine sub( a, b, c ) ! implicit none real :: a, b, c b = a**2 c = a**2 + b end subroutine sub |
Weitere Anweisungen für die UnterprogrammtechnikBearbeiten
Das intent
-AttributBearbeiten
Im Vereinbarungsteil der Funktion wird der Parameterdeklaration das Schlüsselwort intent
mitgegeben.
datentyp, intent( in ) :: var
Mit intent( in )
wird angezeigt, dass der Parameter var
in der Funktion nicht geändert wird und kein Rückfluss der Information in die aufrufende Programmeinheit stattfindet. Ein intent( out )
oder intent( inout )
wie bei Subroutinen wäre meist auch möglich, widerspricht aber dem Grundgedanken des Fortran-Funktionsbegriffs und der strukurierten Programmierung. Bei einer Funktion soll der Informationsrückfluss über den Rückgabewert stattfinden und nicht über Parameter.
Bei der Parameterübergabe bietet eine Subroutine folgende intent
-Möglichkeiten:
- datentyp, intent(in) :: var ... Informationfluss von der aufrufenden Programmeinheit in die Funktion
- datentyp, intent(out) :: var ... Informationfluss von der Subroutine zur aufrufenden Programmeinheit
- datentyp, intent(inout) :: var ... beidseitiger Informationsfluss
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none real :: a, b, c a = 1.0 b = 2.0 c = 3.0 call sub(a, b, c) write (*,*) 'Hauptprogramm: ', a, b, c ! Ausgabe: 1.000000 22.20000 33.30000 end subroutine sub(x, y, z) implicit none real, intent(in) :: x real, intent(out) :: y real, intent(inout) :: z write (*,*) 'Unterprogramm: ', x, y, z ! Ausgabe: 1.000000 2.000000 3.000000 y = 22.2 z = 33.3 end subroutine sub |
Die aktuellen und formalen Parameter müssen hinsichtlich Datentyp, Anzahl, Reihenfolge übereinstimmen.
Die pure
AnweisungBearbeiten
Ein Unterprogramm welches keine Seiteneffekte hat ist ein bloßes bzw. reines (pure) Unterprogramm. Ein Unterprogramm erzeugt dann keine Seiteneffekte, wenn es weder seine Eingabedaten, noch die Daten verändert, die außerhalb des Unterprogrammes liegen, es sei denn, es wären seine Ausgabedaten. In einem reinen Unterprogramm haben die lokalen Variablen keine save
Attribute, noch werden die lokalen Variablen in der Datendeklaration initialisiert.
Reine Unterprogramme sind für das forall
-Konstrukt notwendig: das forall
-Konstrukt wurde für das parallele Rechnen konzipiert, weshalb hier der Computer entscheidet, wie das Konstrukt abgearbeitet werden soll. Dazu ist es aber notwendig, das es egal ist in welcher Reihenfolge das Konstrukt abgearbeitet wird. Gilt dies nicht - hat das Unterprogramm also Seiteneffekte - so kann das forall
-Konstrukt nicht verwendet werden.
Jedes Ein- und Ausgabeargument in einem reinen Unterprogramm muss mittels des intent
-Attributs deklariert werden. Darüberhinaus muss jedes Unterprogramm, das von einem reinen Unterprogramm aufgerufen werden soll, ebenfalls ein reines Unterprogramm sein. Sonst ist das aufrufende Unterprogramm kein reines Unterprogramm mehr.
Die elemental
AnweisungBearbeiten
Ein Unterprogramm ist elementar, wenn es als Eingabewerte sowohl Skalare als auch Felder akzeptiert. Ist der Eingabewert ein Skalar, so liefert ein elementares Unterprogramm einen Skalar als Ausgabewert. Ist der Eingabewert ein Feld, so ist der Ausgabewert ebenfalls ein Feld.
Die return
AnweisungBearbeiten
Eine return
-Anweisung darf nur im Gültigkeitsbereich von Unterprogrammen verwendet werden. Sie bewirkt einen Abbruch der Unterprogrammausführung und eine Rückkehr zum Aufrufpunkt, wo mit der nächsten Anweisung fortgesetzt wird.
Beispiel:
Fortran 90/95-Code (free source form) |
program main implicit none call xyz( -2 ) write( *, * ) "UP-Ende" stop write( *, * ) "Programmende" contains subroutine xyz( n ) integer, intent( in ) :: n integer :: k do k = n, n+20 write( *, * ) k if( k == 5 ) return end do write( *, * ) "k_max =", k end subroutine xyz ! Ausgabe: ! -2 ! -1 ! 0 ! 1 ! 2 ! 3 ! 4 ! 5 ! UP-Ende end program main |
Einige Compiler erlauben zwecks Programmabbruch auch ein return
anstelle des stop
im Hauptprogramm. Das ist aber nicht standardkonform. Andere Compiler würden solchen Code mit einer Fehlermeldung abweisen, das Programm wäre somit nicht mehr uneingeschränkt portabel.
Ein exit
in der Schleife anstelle der return
-Anweisung würde nur den Schleifendurchlauf abbrechen und die Unterprogrammausführung würde nach der Schleife fortgesetzt.
Felder als ParameterBearbeiten
Beispiel: Übergabe eines ganzen Feldes
Datei bsp.f95:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(3,3) :: feld integer :: cnt, i, j cnt = 1 do i = 1, 3 do j = 1, 3 feld(j,i) = cnt cnt = 1 + cnt end do end do ! Unterprogrammaufruf call sub(feld) ! Ausgabe: 1 2 3 4 5 6 7 8 9 end program bsp |
Datei sub.f95
Fortran 90/95-Code (free source form) |
subroutine sub(arr) implicit none integer, dimension(3,3), intent(in) :: arr write(*,*) arr end subroutine sub |
Beispiel: Übergabe einer Feld-Teilkette
Datei bsp.f95:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer,dimension(3,3) :: feld integer :: cnt, i, j cnt = 1 do i = 1, 3 do j = 1, 3 feld(j,i) = cnt cnt = 1 + cnt end do end do ! Unterprogrammaufruf call sub(feld(1:2,2:3)) ! Ausgabe: 4 5 7 8 end program bsp |
Datei sub.f95
Fortran 90/95-Code (free source form) |
subroutine sub(arr) implicit none integer, dimension(0:1, 0:1), intent(in) :: arr write(*,*) arr end subroutine sub |
Beispiel: Übergabe eines Feld-Einzelelements
Datei bsp.f95:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(3,3) :: feld integer cnt, i, j cnt = 1 do i = 1, 3 do j = 1, 3 feld(j,i) = cnt cnt = 1 + cnt end do end do ! Unterprogrammaufruf call sub(feld(1,2)) ! Ausgabe: 4 end program bsp |
Datei sub.f95
Fortran 90/95-Code (free source form) |
subroutine sub(arr) implicit none integer, intent(in) :: arr write(*,*) arr end subroutine sub |
Prozeduren als ParameterBearbeiten
Auch Prozeduren können als Parameter übergeben werden.
Standardfunktionen (intrinsic functions) werden dazu folgendermaßen im Vereinbarungsteil gekennzeichnet:
intrinsic namensliste |
oder
datentyp, intrinsic :: namensliste |
Eigene Unterprogramme oder Unterprogramme aus Bibliotheken mit:
external namensliste |
oder
datentyp, external :: namensliste |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp real, parameter :: PI=3.1415927 ! intrinsic functions intrinsic sin, cos ! Unterprogrammaufrufe call sub(sin, PI) ! Ausgabe: 0.000000 call sub(cos, PI) ! Ausgabe: -1.000000 end program bsp subroutine sub(funk, x) implicit none real :: funk, x write(*,*) nint(funk(x)*1000)/1000.0 end subroutine sub |
Optionale ParameterBearbeiten
Ab Fortran 90 sind auch optionale Parameter für Unterprogramme erlaubt. Solche Parameter sind durch das Attribut optional
zu kennzeichnen. Auch der aufrufenden Programmeinheit muss diese Parameter-Optionalität bekannt gegeben werden, z.B. über ein Interface. Das aktuelle Vorhandensein eines als optional markierten Parameters beim Unterprogrammaufruf kann im Unterprogramm selbst durch die Funktion present
geprüft werden.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none interface subroutine ab(a, b) integer, intent( in ) :: a integer, intent( in ), optional :: b end subroutine ab end interface call ab( 1 ) call ab( 8, 12 ) ! Ausgabe: ! Nur a gegeben 1 ! Beide Parameter gegeben 8 12 end program bsp subroutine ab(a, b) integer, intent( in ) :: a integer, intent( in ), optional :: b if( present( b ) ) then write (*,*) "Beide Parameter gegeben", a, b else write (*,*) "Nur a gegeben", a end if end subroutine ab |
ModuleBearbeiten
Module ersetzen in Fortran 95 die FORTRAN 77-COMMON
-Blöcke. In einem Modul können Variablen, Konstanten und auch Unterprogramme abgelegt werden. Diese können dann von verschiedenen Programmeinheiten angesprochen werden.
module modulname [implicit none] [save] Deklaration von Variablen, Konstanten [contains Unterprogramme ] end module modulname |
Das Einbinden eines Moduls in eine Programmeinheit geschieht mittels der Anweisung
use modulname [, only liste] |
only
signalisiert, dass nur die in liste
angegebenen Variablen oder Konstanten verwendet werden sollen.
Beispiel:
Fortran 90/95-Code (free source form) |
module m1 implicit none save real, parameter :: PI=3.1415 real :: a end module m1 program bsp use m1 implicit none a = 1.5 write(*,*) 'Hauptprogramm 1: ', PI, a ! Ausgabe: Hauptprogramm 1: 3.141500 1.500000 call sub write(*,*) 'Hauptprogramm 2: ', PI, a ! Ausgabe: Hauptprogramm 2: 3.141500 2.500000 end program bsp subroutine sub use m1 implicit none write(*,*) 'Unterprogramm: ', PI, a ! Ausgabe: Unterprogramm: 3.141500 1.500000 a = 2.5 end subroutine sub |
Das save
-Statement in Modulen stellt sicher, dass die aktuellen Werte von Modulvariablen beim Wechsel zwischen den verschiedenen Programmeinheiten sicher erhalten bleiben.
Rekursiver UnterprogrammaufrufBearbeiten
In Fortran 95 können Unterprogramme (Funktionen, Subroutinen) auch rekursiv aufgerufen werden. Rekursion bedeutet, dass ein Unterprogramm sich selbst wieder aufruft (lat. recurrere oder en. recur ... wiederkehren, zurückkommen).
Beispiel: Berechnung von n! (vereinfacht)
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: zahl, ergebnis, fac write(*,*) "Gib eine Ganzzahl ein: " read(*,*) zahl ergebnis = fac(zahl) write(*,*) "Ergebnis ist: ", ergebnis end program bsp recursive function fac(n) result(zerg) implicit none integer, intent(in) :: n integer :: zerg ! Vereinfacht: Keine Überprüfung ob Überlauf, negative Zahl, etc. zerg = n if (n > 1) then zerg = n * fac(n-1) end if return end function fac |
Der Funktionskopf ist bei diesem Beispiel etwas anders gestaltet als üblich. Während der Intel-Fortran-Compiler auch ein
recursive integer function fac(n)
oder ein
integer recursive function fac(n)
problemlos akzeptiert, wirft der gfortran-Compiler bei diesen Varianten einen Syntaxfehler.
Ein- und AusgabeBearbeiten
readBearbeiten
Die read
-Anweisung dient dem Einlesen von Daten. Typisches Beispiel ist die Dateneingabe mittels Tastatur. Formal sieht eine read
-Anweisung so aus:
read([unit=]unit, [fmt=]fmt [, iostat=iostat] [, advance=advance]) [eingabeliste] |
- unit ... Nummer der Eingabeeinheit (ist systemabhängig), Sternoperator oder auch die einer Datei mittels
open
-Anweisung zugeordnete Nummer. - fmt ... Anweisungsnummer zu einer
format
-Anweisung, Sternoperator oder Formatliste - iostat ... read-Status
- advance ... siehe write
Listengesteuerte Eingabe auf der Standardeingabe (normalerweise die Tastatur):
read (*,*) a, b, c
Alternativ kann das auch so geschrieben werden:
read (unit=*, fmt=*) a, b, c
Beim Intel Fortran Compiler, gfortran und g95 ist auch unit = 5
als stdin
(Tastatur) vorbelegt. Das Einlesen aus Dateien und die Einstellung des Formates werden später erläutert.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: i(5) ! Einlesen in ein Feld (unit ... Standardeingabe, fmt ... listengesteuert) read (*,*) i ! ... end program bsp |
Kurze Erläuterung zu iostat
:
Wert | Erläuterung |
---|---|
0 | kein Fehler |
positiver Wert (systemabhängig) | Fehler |
negativer Wert (systemabhängig) | End Of File und kein Fehler |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: i, st ! Einlesen eines Wertes read (*, *, iostat=st) i ! Ausgabe des IO-Status write (*,*) 'IO-Status:', st end program bsp |
- Ausgabe:
- Für Eingabe: 5 → 0
- Für Eingabe: 5.3 → Positiver Wert = Fehler
Das Einlesen aus Dateien und die Einstellung des Formates werden später erläutert.
writeBearbeiten
Die write
-Anweisung dient der Datenausgabe. Typisches Beispiel ist die Anzeige von Daten auf dem Bildschirm. Formal sieht eine write
-Anweisung so aus:
write([unit=]unit, [fmt=]fmt [, iostat=iostat] [, advance=advance]) [ausgabeliste] |
- unit ... Nummer der Ausgabeeinheit (ist systemabhängig), Sternoperator oder auch die einer Datei mittels
open
-Anweisung zugeordnete Nummer. - fmt ... Anweisungsnummer zu einer
format
-Anweisung, Sternoperator oder Formatliste - iostat ... write-Status
- advance ... nur bei sequentieller formatierter I/O. Formatspezifizierer muss explizit gegeben sein.
'no'
... kein Vorschub des file position pointers zum nächsten Datensatz (z.B. kein Zeilenvorschub).'yes'
... mit Vorschub des file position pointers zum nächsten Datensatz (voreingestellt)
Listengesteuerte Ausgabe auf der Standardausgabe (normalerweise der Bildschirm):
write (*,*) a, b, c
Alternativ kann das auch so geschrieben werden:
write (unit=*, fmt=*) a, b, c
Beim Intel Fortran Compiler, gfortran und g95 sind auch
unit
=0 als stderr (Bildschirm) undunit
=6 als stdout (Bildschirm)
vorbelegt. Bezüglich iostat
gilt auch hier der im vorigen Abschnitt kurz geschilderte Sachverhalt.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: i(5) = (/ 5, 4, 1, -1, -7 /) ! ... ! Ausgabe der Feldwerte (unit ... Standardausgabe, fmt ... listengesteuert) write (*,*) i ! ... end program bsp |
Beispiel: advance
Fortran 90/95-Code (free source form) |
program bsp implicit none character(10) :: ch integer :: st write(*, '(A)', advance='YES') "Hallo" write(*, '(A)', advance='YES') "Welt" write(*, *) "ABCDEFGHIJKLMN" end program bsp |
Ausgabe:
advance='YES' | advance='NO' |
---|---|
Hallo Welt ABCDEFGHIJKLMN |
HalloWelt ABCDEFGHIJKLMN |
Die Ausgabe in Dateien und die Einstellung des Formates werden etwas später erläutert.
Kürzer: print
, read
, write
und NamenslistenBearbeiten
Für die listengesteuerte Ein- und Ausgabe existieren auch vereinfachte Formen. Für Eingaben wird wieder der read
-Befehl verwendet, für Ausgaben gibt es die print
-Anweisung.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: a, b real :: r complex :: z character( len = 10 ) :: str read *, a, b, r, z, str ! Eingabe per Tastatur: ! 10, 30, 55.5, (10.8,7.0), Hallo print *, str, a, b, r, z ! Ausgabe am Bildschirm: ! Hallo 10 30 55.5 (10.8,7.) end program bsp |
Bei mehrfachem Aufruf gleicher Ein- bzw. Ausgabeanweisungen kann durch Verwendung von Namenslisten der Programmcode kürzer gestaltet werden. Die Dateneingabe wird dadurch aber etwas komplizierter:
- eingeleitet wird die Eingabe durch ein &-Zeichen, unmittelbar gefolgt vom Namenslistenbezeichner
- danach folgen ein oder mehrere Leerzeichen
- es folgen die Zuweisungen von Werten zu den Variablennamen
- abgeschlossen wird die Eingabe durch einen Slash /
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: a, b real :: r complex :: z character( len = 10 ) :: str namelist / LISTEA / a, b, r, z, str namelist / LISTEB / str, r, z read( *, nml = LISTEA ) ! Eingabe per Tastatur: ! &LISTEA b = 30, a = 10, r = 55.5, z = (10.8,7.0), str = "Hallo" / write( *, nml = LISTEB ) ! Ausgabe auf dem Bildschirm (Intel 9.1): ! &LISTEB ! STR = Hallo , ! R = 55.50000 , ! Z = (10.80000,7.000000) ! / end program bsp |
FormatierungBearbeiten
Die Ein- und Ausgabeformatierung kann beeinflusst werden. Zu diesem Zweck gibt es die format
-Anweisung.
... (..., fmt = marke, ...) ... marke format (formatliste) |
Alternativ kann die Formatliste auch direkt in die read
- oder write
-Anweisung eingebunden werden
... (..., fmt = '(formatliste)', ...) ... |
FormatlistenelementeBearbeiten
Formatspezifizierer | Kommentar |
---|---|
Ix[.z] | Ganzzahl mit einer Feldlänge von x Zeichen. z gibt die Mindestanzahl der auszugebenden Zeichen an (Feld wird, wenn nötig, mit führenden Nullen aufgefüllt). |
Bx[.z] | Ganzzahl, Ausgabe als Binärzahl. |
Ox[.z] | Ganzzahl, Ausgabe als Oktalzahl. |
Zx[.z] | Ganzzahl, Ausgabe als Hexadezimalzahl. |
Fx.y | Fixkommazahl mit einer Gesamtfeldlänge von x Zeichen. y ist die Anzahl der Nachkommastellen (Vorzeichen und Dezimalpunkt müssen in der Gesamtfeldlänge berücksichtigt werden). F0.y führt zu einer variablen Feldlänge in Abhängigkeit vom minimalen Platz der für die Vorkommastellen nötig ist. |
Ex.y | Gleitkommazahl mit einer Gesamtfeldlänge von x Zeichen. y ist die Anzahl der Nachkommastellen. (Vorzeichen, Dezimalpunkt und die Zeichen für den Exponenten müssen in der Gesamtfeldlänge berücksichtigt werden). |
Dx.y | Gleitkommazahl, doppelte Genauigkeit |
A | Eine Zeichenkette. |
Ax | Eine Zeichenkette mit x Zeichen. |
Lx | Ein logischer Wert, T (wahr) bzw. F (falsch). |
xX | x Leerzeichen. |
/ | Zeilenvorschub |
Tx | Tabulator (an der Position x) |
Obige Tabelle der Formatlistenelemente ist nicht vollständig; es gibt z.B. noch »G« für ein »generelles« Ausgabeformat, das aber wenig gebräuchlich ist. Die Ausgabe erfolgt normalerweise rechtsbündig. Reicht die Gesamtfeldlänge bei numerischen Werten nicht aus, so werden anstelle einer Zahl Sternchen angezeigt.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: a a = 999 write(*, 3333) a ! Ausgabe: 999 a = -999 write (*, 3333) a ! Ausgabe: *** 3333 FORMAT (I3, /, /) ! / ... nach jeder 3333-write-Anweisung werden zwei Leerzeilen eingefügt end program bsp |
Alternativ könnte die Formatliste auch so in die write
-Anweisung eingebaut werden:
write(*, '(I3, /, /)') a
Oder mittels benannter Konstante auch so:
character(*), parameter :: fs = '(I3, /, /)' print fs, a
Hier wurde statt der write
-Anweisung der print
-Befehl verwendet. Der Effekt ist derselbe, es erfolgt eine formatierte Ausgabe.
Weitere Formatierungsbeispiele:
Code | Ausgabe |
---|---|
WRITE(*, 999) 1234 WRITE(*, 999) 1234567 WRITE(*, 999) 1234567890 999 FORMAT(I9.6) |
001234 1234567 ********* |
WRITE(*, 999) 555.6666 WRITE(*, 999) +5.6 WRITE(*, 999) -55.666E7 WRITE(*, 999) -55555.666 999 FORMAT(F9.3) |
555.667 5.600 ********* ********* |
WRITE(*, 999) 555.6666 WRITE(*, 999) +5.6 WRITE(*, 999) -55.666E7 WRITE(*, 999) -55555.666 999 FORMAT(E9.3) |
0.556E+03 0.560E+01 -.557E+09 -.556E+05 |
WRITE(*, 999) 'Hallo' WRITE(*, 999) 'ABCDEFGHIJKL' WRITE(*, 888) 'ABCDEFGHIJKL' 888 FORMAT(A) 999 FORMAT(A10) |
Hallo ABCDEFGHIJ ABCDEFGHIJKL |
WRITE(*, *) 'FORTRAN', '77' WRITE(*, 999) 'FORTRAN', '77' 999 FORMAT(A, 1X, A) |
FORTRAN77 FORTRAN 77 |
WRITE(*, 888) 'FORTRAN', '77' WRITE(*, 999) 'FORTRAN', '77' 888 FORMAT(A, T3, A) 999 FORMAT(A, T20, A) |
FO77RAN FORTRAN 77 |
WRITE(*, 999) 'FORTRAN', '77' 999 FORMAT(A, /, A) |
FORTRAN 77 |
WRITE(*, 999) 34.56 WRITE(*, *) 34.56 C SP ... Sign Plus (+) 999 FORMAT(SP, F12.3) |
+34.560 34.56 |
Wiederholung von FormatteilenBearbeiten
Beispiel:
write(*, 100) 'abc', 10.3, 'xxx', 23.4 100 format (2(A3, F6.1))
write etwas andersBearbeiten
Beispiel:
write (*, 100) 100 format ('Hallo', X, 'Welt!')
Dynamische MehrfachformatierungBearbeiten
Formatierungsanweisungen können auch als String bearbeitet werden, indem man z.B. die Anzahl der auszugebenden Variablen per write-Befehl in die Formatierung schreibt.
Mehrfachformatierung - Beispiel:
character(5) :: formatierung integer, dimension(1:4) :: einsen = 1 integer :: anzahl anzahl = 4 formatierung = '( I1)' write(formatierung(2:2), '(I1)') anzahl write(*, formatierung) einsen
DateienBearbeiten
DatensatzBearbeiten
Datensätze können in folgender Form auftreten:
- Formatierter Datensatz: Textdatensatz
- Unformatierter Datensatz: Datensatz in einer maschineninternen Form.
- Dateiendesatz
DateiBearbeiten
Für Fortran ist alles eine Datei, das durch read
oder write
bearbeitbar ist.
Zugriffsmethoden:
- Sequentieller Zugriff: Lesen ab Beginn der Datei (file) und dann immer den nächsten Datensatz einlesen. Geschrieben wird jeweils ans Dateiende. Auf interne Dateien kann nur sequentiell zugegriffen werden.
- Direkter Zugriff: Bearbeiten in beliebiger Reihenfolge durch Angabe der Satznummer.
- Binärer Zugriff: Bearbeiten von Dateien, die binäre Daten enthalten, z. B. Bilder von CCD-Kamera, Scilab/Matlab
save
-Dateien
Dateitypen:
- Externe Datei: Eine konventionelle Datei
- Interne Datei: character-Variable oder -Feld.
Dateien haben im Betriebsystem einen Dateinamen. In Fortran wird eine Datei über eine Dateinummer (unit) angesprochen. Die Zuordnung erfolgt mit dem Befehl open
.
openBearbeiten
Zum Öffnen einer externen Datei dient die open
-Anweisung.
open (liste) |
mit folgender liste
Element | Kommentar |
---|---|
[unit =] x | x ist eine Dateinummer (Ganzzahl, sollte über 10 liegen, da oft Nummern unter 10 fix zugeordnet sind, z.B. der Standardein-, ausgabe). |
file = x | x ist der externe Dateiname |
iostat = x | x ist 0 wenn open fehlerfrei ausgeführt wurde, ansonsten eine systemabhängige Fehlernummer |
status = x | Dateistatus: 'old' ... Datei existiert bereits 'new' ... Datei wird neu erzeugt 'scratch' ... namenlose temporäre Datei 'unknown' ... System bestimmt Dateistatus selbst 'replace' ... der Inhalt einer bereits vorhandenen Datei wird gelöscht. |
access = x | Zugriffsmethode: 'sequential' ... Sequentielle Datei 'direct' ... direkter Zugriff 'stream' ... binärer Zugriff |
position = x | Den Dateisatzzeiger beim Öffnen der Datei an eine bestimmte Position setzen. ('asis', 'rewind', 'append') |
form = x | Format: 'formatted' oder 'unformatted' |
action = x | 'read' ... nur Lesezugriff 'write' ... nur Schreibzugriff 'readwrite' ... Lesen und Schreiben |
recl = x | Datensatzlänge (positive Zahl, access='direct', in Bytes) |
err = x | Im Fehlerfall Sprung zur Marke x |
blank = x | 'null' oder 'zero' (nur für form='formatted') |
delim = x | 'apostrophe' 'quote' 'none' |
pad = x | 'yes' oder 'no' (nur für form='formatted') |
Eingestellte Vorgabewerte sind:
- status = 'unknown'
- position = 'asis'
- access = 'sequential'
- form = 'formatted'
Wird access='direct'
gesetzt, so gilt form='unformatted'
als Vorgabewert.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: stat character(80) :: str open(20, file='/tmp/testdatei.txt', iostat=stat) if(stat == 0) then write(*,*) 'Das Öffnen der Datei war ein voller Erfolg' do read(20, '(A)', iostat=stat) str ! Bei EOF wird stat /= 0 if (stat /= 0) exit write(*,*) str end do else write(*,*) 'Datei konnte nicht geöffnet werden' end if close(20) end program bsp |
closeBearbeiten
Geschlossen wird die Verbindung zur externen Datei mit dem close
-Befehl.
close (liste) |
liste:
Element | Kommentar |
---|---|
[unit =] x | wie bei open |
iostat = x | wie bei open |
err = x | wie bei open |
status = x | 'keep' ... Datei erhalten (voreingestellt) 'delete' ... Datei löschen |
Lesen und SchreibenBearbeiten
Gelesen oder geschrieben wird mit den bereits bekannten read
- und write
-Anweisungen.
Element | Kommentar |
---|---|
[unit =] x | Dateinummer bzw. CHARACTER-Variable oder Feld (interne Datei) |
[fmt =] x | siehe Formatierung |
[nml] = x | x ... namelist-group-name |
rec = x | Datensatznummer bei Direktzugriff (siehe Abschnitt Direktzugriff) |
iostat = x | wie bei read
|
err = x | Bei Fehler Sprung zur Anweisungsnummer x |
end = x | Bei Dateiende Sprung zur Anweisungsnummer x (nicht erlaubt bei Direktzugriff, nicht bei write) |
advance = x | 'yes' oder 'no' |
eor = x | Bei End of Record Sprung zur Marke x (nicht bei write) |
size = x | x ... Zeichenzähler (nicht bei write, advance='no') |
Es existiert eine Menge von Einschränkungen, wann welche Elemente erlaubt sind, bzw. welche nur kombiniert auftreten sollen, z.B.
- wenn der
rec
-Spezifizierer Verwendung findet, dann darf keinend
-Element angegeben werden - Bei Dateneingabe nur dann ein
size
-Spezifizierer, wennadvance='no'
gesetzt ist.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none character (len = 80) :: a integer :: st = 0 open (20, file='/tmp/testdatei.txt', status='OLD', iostat=st) if (st /= 0) then stop "open-Fehler!" end if ! Aus Datei lesen do read (20, 888, iostat=st) a ! Auf Standardausgabe schreiben if (st == 0) then write (*, 888) a else if (st > 0) then write (*,*) "read-Fehler!" exit else if (st < 0) then exit end if end do close(20) 888 format(A) end program bsp |
DirektzugriffBearbeiten
OPEN:
Element | Kommentar |
---|---|
access = x | x ... 'DIRECT' |
recl = x | x ... Datensatzlänge (positive Zahl, access='DIRECT', in Bytes bzw. bei formatierten Dateien in Characters) |
fmt = x | x ... Formatangabe (wird eine Datei 'FORMATTED' geöffnet, dann muss auch eine konkrete Formatliste angegeben werden, ansonsten tut's auch der Sternoperator) |
READ/WRITE:
Element | Kommentar |
---|---|
REC = x | x ... Satznummer bei Direktzugriff |
Beispiel: Gegeben ist die Textdatei /tmp/testdatei.txt
mit dem Inhalt
Die WRITE-Anweisung dient der Datenausgabe aus einem FORTRAN-Programm auf ein externes Gerät. Typisches Beispiel ist die Anzeige von Daten auf dem Bildschirm. Formal sieht eine WRITE-Anweisung so aus:
Fortran 90/95-Code (free source form) |
program bsp implicit none character (len = 10) :: c integer :: st open (20, file='/tmp/testdatei.txt', status='OLD', form='FORMATTED', & access='DIRECT', recl=15, iostat=st) if (st /= 0) then stop "open-Fehler!" end if read (20, fmt='(A)', rec=4, iostat=st) c if (st /= 0) then write (*,*) "read-Error" else write (*,*) c end if close (20) end program bsp |
Ausgabe:
s einem FO
Positionieren bei sequentiellen DateienBearbeiten
Datensatzzeiger um einen Datensatz zurücksetzen:
backspace ([unit=]x [,iostat=y] [,err=z]) |
Positionieren an den Dateibeginn:
rewind ([unit=]x [,iostat=y] [,err=z]) |
Schreiben eines Dateiendsatzes:
endfile ([unit=]x [,iostat=y] [,err=z]) |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none character (len = 100), dimension(3) :: c integer :: st = 0 open (20, file='/tmp/testx.txt', status='NEW', iostat=st) call checkStatus(st, "open-") write (20,*) 'Das ist eine Testdatei' write (20,*) 'Dies ist Zeile 2 der Testdatei' write (20,*) 'Jenes die Zeile 3 der Testdatei' write (20,*) "Jetzt ist's aber genug" endfile (20) rewind (20, iostat=st) call checkStatus(st, "rewind-") read (20, fmt=555, iostat=st) c call checkStatus(st, "read-") write (*, fmt=555) c backspace (20, iostat=st) call checkStatus(st, "backspace-") read (20, fmt=555, iostat=st) c(1) call checkStatus(st, "read-") write (*, fmt=555) c(1) close (20) 555 format (A) end program bsp subroutine checkStatus(st, ch) integer, intent (in) :: st character (*), intent (in) :: ch if (st /= 0) then close(20) write (*,*) ch // "Fehler!" stop end if end subroutine checkStatus |
Ausgabe:
Das ist eine Testdatei Dies ist Zeile 2 der Testdatei Jenes die Zeile 3 der Testdatei Jenes die Zeile 3 der Testdatei
inquireBearbeiten
Die Anweisung inquire
dient der Abfrage einiger Eigenschaften von Dateien oder I/O-Units.
inquire (file = x, liste) |
mit x ... Dateiname (inkl. Pfad)
inquire ([unit =] x, liste) |
mit x ... Nummer der I/O-Unit.
liste:
Element | Kommentar |
---|---|
access = x | x:
|
action = x | x:
|
blank = x | x:
|
delim = x | x:
|
direct = x | x:
|
err = x | Bei Fehler Sprung zur Anweisungsnummer x |
exist = x | x:
|
form = x | x:
|
formatted = x | x:
|
iostat = x | x ist 0 wenn OPEN fehlerfrei ausgeführt wurde, ansonsten eine systemabhängige positive Fehlernummer |
name = x | Der Dateiname wird der Zeichenketten-Variablen x zugewiesen. Hat die Datei keinen Namen, dann ist das Ergebnis undefiniert. |
named = x | x:
|
nextrec = x | x ... Nummer des nächsten Datensatzes |
number = x | x ... Nummer der mit einer externen Datei verbundenen I/O-Unit. |
opened = x | x:
|
pad = x | x:
|
position = x | x:
|
read = x | x:
|
readwrite = x | x:
|
recl = x | x ... Datensatzlänge bei Direktzugriff |
sequential = x | x:
|
unformatted = x | x:
|
write = x | x:
|
Beispiel: Datei vorhanden?
Fortran 90/95-Code (free source form) |
program bsp implicit none logical :: l integer :: st inquire (file='/tmp/testdatei.txt', exist=l, iostat=st) if (st == 0) then write (*,*) "Datei existiert?", l else write(*,*) "Fehler!" end if ! wenn Datei existiert: Datei existiert? T ! wenn Datei nicht existiert: Datei existiert? F ! wenn aus irgendeinem ein inquire-Fehler auftrat: Fehler! end program bsp |
Beispiel: Infos zu einer geöffneten Datei
Fortran 90/95-Code (free source form) |
program bsp implicit none logical :: ex character (15) :: di, fo, ac, se integer :: nu, st open (25, file='/tmp/testdatei.txt', status='old', iostat=st) if(st /= 0) stop "open-Fehler!" inquire (25, exist = ex, direct = di, sequential = se, formatted = fo, & access = ac, number = nu, iostat=st) if(st == 0) then write (*,*) 'EXIST? ', ex write (*,*) 'DIRECT? ', di write (*,*) 'SEQUENTIAL? ', se write (*,*) 'FORMATTED? ', fo write (*,*) 'ACCESS? ', ac write (*,*) 'NUMBER? ', nu else write (*,*) "inquire-Fehler!" end if close(25) ! Ausgabe, z.B. ! EXIST? T ! DIRECT? YES ! SEQUENTIAL? YES ! FORMATTED? YES ! ACCESS? SEQUENTIAL ! NUMBER? 25 end program bsp |
Interne DateienBearbeiten
- Interne Dateien sind vom Datentyp
character
(Zeichen oder Zeichenketten) - Das Lesen aus bzw. das Schreiben in interne Dateien erfolgt immer sequentiell
Beispiel: Schreiben in eine interne Datei
Fortran 90/95-Code (free source form) |
program bsp character(15) :: ch real :: r = 12.5678 ! Interne Datei "ch" write (ch, *) r write (*,*) 'r lexikalisch groesser als Buchstabe "A"? ', lge(ch, 'A') end program bsp |
Beispiel: Lesen aus einer internen Datei
Fortran 90/95-Code (free source form) |
program bsp character(15) :: ch = '12.5678' real :: r ! Interne Datei "ch" read (ch, '(F15.5)') r write (*,*) 'r = ', r write (*,*) 'r**2 = ', r**2 end program bsp |
ZeigerBearbeiten
Was sind Zeiger?Bearbeiten
Ein Zeiger (Pointer) ist eine Variable, die Adresse und (Daten-)Typ enthält – ähnlich wie bei einem Array, das rank und shape enthält. Der Zeiger (Pointer) charakterisiert Position, Länge und Organisation des Speicherabschnitts, an dem die Variable (Target) gehalten wird.
Zeiger in Fortran 95Bearbeiten
In Fortran 95 werden Zeiger durch Zufügen des Attributes pointer
bei der Deklaration von Variablen erzeugt.
datentyp, pointer :: variable |
Ein so deklarierter Zeiger kann auf andere Zeiger oder auf mittels target
gekennzeichnete Variablen verweisen.
datentyp, target :: variable |
Die Zeigerzuordnung erfolgt durch das Symbol
=> |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none real, pointer :: ptr real, target :: trg trg = 5.5 ptr => trg write(*,*) ptr ! Ausgabe: 5.50000 ! Zeiger werden bei Bedarf automatisch dereferenziert end program bsp |
AssoziationsstatusBearbeiten
Ein Zeiger kann einen der folgenden Assoziationzustände annehmen:
- undefiniert (dangling)
- nicht zugeordnet (disassociated, null)
- zugeordnet (associated)
Der Zuordnungsstatus eines Zeigers ist unmittelbar nach der Deklaration undefiniert. Mittels zeiger => null()
oder nullify(zeiger)
kann ein Zeiger auf einen nicht zugeordneten Status gesetzt werden. Verweist ein Zeiger auf einen anderen zugeordneten Zeiger oder ein Target, so ist sein Zustand zugeordnet.
Der Assoziationsstatus eines Zeigers lässt sich über die Funktion
associated (zeiger [, ziel])
abfragen. Sinnvoll ist eine derartige Abfrage nur dann, wenn der Zuordnungsstatus nicht undefiniert ist.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, pointer :: ptr1 => null() character(20), pointer :: ptr2 character(20), target :: str str = "Hallo, Welt!" ptr2 => str write(*,*) associated(ptr1) ! Ausgabe: F write(*,*) associated(ptr2) ! Ausgabe: T write(*,*) associated(ptr2, ptr1) ! Ausgabe: F write(*,*) associated(ptr2, str) ! Ausgabe: T end program bsp |
Speicherplatz dynamisch reservieren und freigebenBearbeiten
Für normale Variablen läuft die Speicherplatzverwaltung automatisch ab. Bisher wurden Zeiger immer solchen normalen (Target)Variablen, für die bereits Speicherplatz reserviert war, zugeordnet. Aber auch für Zeiger selbst kann Speicherplatz reserviert werden. Bei der Zeigerdeklaration ist der Zeigerstatus undefiniert oder nicht zugeordnet. Eine Wertzuweisung an eine solche Zeigervariable würde zur Laufzeit einen Speicherzugriffsfehler ergeben. Die Funktion
allocate (zeiger1, [zeiger2, ...] [,stat=integervar])
reserviert in Abhängigkeit des Zeiger-Datentyps Speicherplatz für die einzelnen Zeiger. Die Funktion
deallocate (zeiger1, [zeiger2, ...] [,stat=integervar])
gibt diesen Speicherplatz wieder frei.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, pointer :: ptr1 => null(), ptr2 => null() integer :: status allocate(ptr1, stat = status) ptr1 = 2222 write (*,*) "Status = ", status ! Wenn status = 0, dann wurde erfolgreich Speicherplatz reserviert write (*,*) ptr1 ! Ausgabe: 2222 ptr2 => ptr1 ptr1 = 5555 write (*,*) ptr1, ptr2 ! Ausgabe: 5555 5555 deallocate(ptr1) end program bsp |
Zeiger und FelderBearbeiten
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(:), pointer :: ptr => null() integer, dimension(5:10), target :: arr = (/55, 66, 77, 88, 99, 111/) ptr => arr write(*,*) ptr ! Ausgabe: 55 66 77 88 99 111 ptr => arr(7:) write(*,*) ptr ! Ausgabe: 77 88 99 111 end program bsp |
Beispiel: "ragged array"
Fortran 90/95-Code (free source form) |
program bsp implicit none type element integer, dimension(:), pointer :: ptr end type element integer, dimension(5), target :: a = (/ 1, 2, 3, 4, 5 /) integer, dimension(2), target :: b = (/ 99, 55 /) integer, dimension(4), target :: c = (/ -11, -12, -13, -14 /) integer :: i type( element ), dimension(3) :: rarr rarr(1)%ptr => a rarr(2)%ptr => b rarr(3)%ptr => c do i = 1,3 write (*,*) "rarr(", i, "): ", rarr(i)%ptr end do ! Ausgabe ! rarr( 1 ): 1 2 3 4 5 ! rarr( 2 ): 99 55 ! rarr( 3 ): -11 -12 -13 -14 end program bsp |
Verkettete ListenBearbeiten
In der Regel ist die Größe einer Liste vor ihrer Erstellung (zum Bsp. durch Einlesen einer Datei) nicht bekannt, was die Verwendung von Array erschwert. Dies lässt sich durch Pointer vereinfachen. Man unterscheidet dabei viele verschiedene Listentypen:
- linear und einfach verkettet: Diese Liste lässt sich nur in eine Richtung durchlaufen und das Ende hat keinen Bezug zum Anfang.
- zyklisch und einfachverkettet: Nur eine Durchlaufrichtung, aber das Ende ist mit dem Anfang verpointert. Nach dem vollständigen Durchlaufen beginnt man wieder am Anfang.
- linear und doppelt verkettet: Lineare Liste, in der jedes Element auf das nächste UND das vorherige zeigt. Der Durchlauf der Liste ist so in zwei Richtungen möglich. Der Anfang ist dabei nicht mit den Ende verpointert.
- zyklisch und doppelt verkettet: Zyklische Liste innerhalb derer zwei Durchlaufrichtungen zur Verfügung stehen.
Die Wahl des Listentyps richtet sich nach dem Verwendungszweck. Einfach verkettete Listen sind auch einfach in der Implementierung. Für den Zugriff entgegen der Durchlaufrichtung muss die Liste jedoch einmal vollständig durchlaufen werden, was den Rechenaufwand und vor allem die Rechenzeit erhöht. Sie eignen sich daher gut für einfache Aufgaben mit gerichteter Abarbeitung. Doppelt verkettete Listen lassen mehr Freiraum hinsichtlich ihr Durchlaufrichtung. Sie sind jedoch komplexer in Ihrer Implementierung und erhöhen den Speicheraufwand.
Anfügen von ListenelementenBearbeiten
Beispiel: Lineare, einfach verkettete Liste (LIFO)
Fortran 90/95-Code (free source form) |
module m1 implicit none type node integer :: id character(5) :: value type(node), pointer :: next => null() end type type(node), pointer :: first => null() private ! Auf alle nachfolgenden Anweisungen kann von aussen nicht zugegriffen werden public :: add_node, write_all, free_all ! Auf die Subroutinen add_node, write_all und free_all ! kann von aussen explizit zugegriffen werden, ! jedoch nicht auf innerhalb der Subroutinen ! deklarierte Datentypen contains subroutine add_node(id, str) implicit none integer, intent(in) :: id character(5), intent(in) :: str type(node), pointer :: new, tmp ! Speicher reservieren allocate(new) ! Werte setzen new%id = id new%value = str ! Am Beginn der Liste einfügen if (.not. associated(first)) then first => new else tmp => first first => new first%next => tmp end if end subroutine add_node subroutine write_all() implicit none type(node), pointer :: tmp tmp => first do if (associated(tmp) .eqv. .FALSE.) exit write(*,*)tmp%id, tmp%value tmp => tmp%next end do end subroutine write_all subroutine free_all() implicit none type(node), pointer :: tmp do tmp => first if (associated(tmp) .eqv. .FALSE.) exit first => first%next deallocate(tmp) end do end subroutine free_all end module m1 program bsp use m1 implicit none call add_node (1, "AAAAA") call add_node (2, "BBBBB") ! ... call add_node (150, "ZZZZZ") call write_all ! Ausgabe: ! 150 ZZZZZ ! 2 BBBBB ! 1 AAAAA call free_all ! Die verkettete Liste wird wieder freigegeben end program bsp |
Beispiel: Zyklische einfach verkettete Liste:
Fortran 90/95-Code (free source form) |
module m1 implicit none private public: ... type node integer :: id character(5) :: value type(node), pointer :: next => null() end type type(node), pointer :: last => null() !externer Zeiger auf die zyklische Liste ... ! Vergleich oben, subroutine add_node(id, str) subroutine add_node(id, str) implicit none integer, intent(in) :: id character(5), intent(in) :: str type(node), pointer :: new, tmp ! Speicher reservieren allocate(new) ! Werte setzen new%id = id new%value = str ! Listen Elemente werden in Reihenfolge des Einlesen verpointert if (.not. associated(last)) then last => new new%next => new ! das erste Element muss auf sich selbst zeigen für den zkylus else tmp => last last => new new%next => tmp%next tmp%next => new end if end subroutine ... |
Löschen von ListenelementenBearbeiten
Für das Löschen eines Elementes aus einer Liste lässt sich eine kurze Subroutine schreiben, welche die Zeiger neu setzt und das Element löscht. Man kann dabei in einfach verketteten Listen nicht ohne größeren Aufwand das Element löschen, welches soeben in der Liste betrachtet wird, sondern nur das nachfolgende Element.
Fortran 90/95-Code (free source form) |
SUBROUTINE del_next(pntr) TYPE(liste),POINTER :: pntr !Subroutine bekommt einen externen Pointer übergeben TYPE(liste),POINTER :: tmp => NULL() tmp => pntr ! pntr wird in tmp zwischengespeichert pntr => pntr%next ! pntr wird um zwei Postionen weitergesetzt pntr => pntr%next DEALLOCATE(tmp%next) ! tmp%next wird gelöscht tmp%next => pntr ! Verbindung wird neu gesetzt END SUBROUTINE |
Vektoren- und MatrizenrechnungBearbeiten
In Fortran95 können einige elementare Vektoren- und Matrizenoperationen sehr einfach ausgeführt werden.
Beispiel: Addition und Subtraktion von Vektoren (Matrizen)
Fortran 90/95-Code (free source form) |
program bsp implicit none real, dimension(3) :: a = (/3, 2, -5/), b = (/1, -3, -1/) write(*,*) "a+b: ", a+b ! Ausgabe: a+b: 4. -1. -6. write(*,*) "a-b: ", a-b ! Ausgabe: a-b: 2. 5. -4. end program bsp |
Beispiel: Multiplikation eines Vektors (einer Matrix) mit einem Skalar
Fortran 90/95-Code (free source form) |
program bsp implicit none real, dimension(3) :: a = (/3, 2, -5/) write(*,*) "3.5*a: ", 3.5*a ! Ausgabe: 3.5*a: 10.5 7. -17.5 end program bsp |
Beispiel: Skalarprodukt
Fortran 90/95-Code (free source form) |
program bsp implicit none real, dimension(3) :: a = (/3, 2, -5/), b = (/1, -3, -1/) real :: dot_product write(*,*) "a.b: ", dot_product(a, b) ! Ausgabe: 2 end program bsp |
Beispiel: Euklidische Norm eines Vektors
Fortran 90/95-Code (free source form) |
program bsp implicit none real, dimension(3) :: x = (/3, 2, -5/) real :: x_n ! Norm des Vektors x x_n = sqrt(sum(x**2)) write(*,*) "Die Norm des Vektors (", x, ") beträgt: ", x_n ! Ausgabe: Die Norm des Vektors ( 3.0 2.0 -5.0 ) beträgt: 6.164414 end program bsp |
Beispiel: Matrizenmultiplikation
Fortran 90/95-Code (free source form) |
program bsp implicit none real, dimension(3,2) :: A = reshape( (/3., 2., 1., 1., 1., 2.5/), (/3, 2/) ) real, dimension(2,2) :: B = reshape( (/1., -3., -1., 5./), (/2, 2/) ) real, dimension(3,2) :: C C = matmul(A, B) write(*,*) "Matrix C =" write(*,*) C(1, :) write(*,*) C(2, :) write(*,*) C(3, :) ! Ausgabe: Matrix C = ! 0.000000 2.000000 ! -1.000000 3.000000 ! -6.500000 11.50000 end program bsp |
Beispiel: Transponierte Matrix
Fortran 90/95-Code (free source form) |
program bsp implicit none real, dimension(2,2) :: A = reshape( (/3., 2., 1., -1.5/), (/2, 2/) ), AT AT = transpose(A) write(*,*) "A =" write(*,*) A(1, :) write(*,*) A(2, :) ! Ausgabe: A = ! 3.000000 1.000000 ! 2.000000 -1.500000 write(*,*) "AT =" write(*,*) AT(1, :) write(*,*) AT(2, :) ! Ausgabe: AT = ! 3.000000 2.000000 ! 1.000000 -1.500000 end program bsp |
SystemroutinenBearbeiten
Datum und ZeitBearbeiten
Die Subroutine
date_and_time(datum,zeit)
liefert die Systemzeit in der Form YYYYMMTT und HHMMSS.SSS zurück. datum
und zeit
sind Character von mindestens 8 bzw. 10 Zeichen Länge.
Die vom Programm verbrauchte Rechenzeit (Prozessorzeit) in Sekunden liefert die
Subroutine (zeit
ist vom Typ Real)
cpu_time(zeit)
ZufallszahlenBearbeiten
Die Subroutine
random_number(r)
schreibt gleichverteilte Zufallszahlen im Intervall [0,1) in eine Variable r
vom Typ Real (Skalar oder Feld). Mit der Subroutine
random_seed()
kann der Zufallszahlengenerator (zufällig) initialisiert werden.
KommandozeilenargumenteBearbeiten
Nicht unbedingt Standard, aber bei etlichen Fortran-Compilern doch als Standardfunktion implementiert, sind die Prozeduren iargc
und getarg
zum Erfragen der beim Programmstart mitgegebenen Kommandozeilenargumente.
Die Funktion
i = iargc()
liefert die Anzahl der Kommandozeilenargumente. Der Programmname selbst wird dabei nicht mitgezählt.
Die Subroutine
getarg(i, c)
liefert den Wert eines bestimmten Kommandozeilenargumentes. i
gibt die Position vor (0 ...Programmname, 1 ... 1.Argument, etc.).
Der Parameter c
ist vom Typ Character. Dort findet sich nach Abarbeitung der Subroutine der zu i
gehörende Wert des Kommandozeilenargumentes.
Von der modularen zur objektorientierten ProgrammierungBearbeiten
Module im DetailBearbeiten
Die Bezeichnung des Schlüsselworts module
weist schon darauf hin, dass Fortran 90/95 eine modulare Softwareentwicklung ermöglicht.
Modulare ProgrammierungBearbeiten
Was ist modulare Programmierung? Modulare Programmierung
Das Modul-Konzept in Fortran 90/95 unterstützt diesen Ansatz vollständig. Das module
-Konstrukt gliedert sich schematisch so
module ... |
|
|
end module ... |
Vor dem Datenbereich können noch einige Deklarationen (implicit none, save
, etc.) eingefügt sein. Der Methodenbereich wird durch das Schlüsselwort contains
angekündigt oder als Interface deklariert. Aber prinzipiell gilt, dass zusammengehörende Daten und die dazugehörenden Methoden (Unterprogramme) in einem Modul zusammengefasst werden.
ZugriffsteuerungBearbeiten
Durch Angabe des Schlüsselworts private
lässt sich die Sichtbarkeit von Datenelementen einschränken. Auf solcherart deklarierte Variablen läßt sich außerhalb des Moduls nicht zugreifen. public
erlaubt den Zugriff auf entsprechend deklarierte Variablen auch von außerhalb. Zweiteres ist Standardverhalten und das Schlüsselwort public
muss somit nicht explizit angegeben werden.
Beispiel:
Fortran 90/95-Code (free source form) |
module mod_bsp implicit none save ! Datenbereich real, private :: x = 1.2 real :: y = 9.8 contains ! Methodenbereich real function addX (a) real, intent (in) :: a addX = x + a end function addX end module mod_bsp program bsp use mod_bsp implicit none write (*,*) "Ergebnis1 = ", addX (2.1) write (*,*) "Ergebnis2 = ", y + 2.1 ! Ausgabe: ! Ergebnis1 = 3.300000 ! Ergebnis2 = 11.90000 ! Folgendes geht nicht -> Fehlermeldung: ! write (*,*) "Ergebnis3 = ", x + 2.1 ! 1) x ist private und somit außerhalb des Moduls nicht bekannt ! 2) auch im Hauptprogramm selbst ist kein x deklariert (implicit none, ! es wäre ohnehin nicht die gleiche Variable wie im Modul mod_bsp) end program bsp |
Rein formal läßt sich der Zugriff auf die Daten und/oder Methoden eines Moduls (module procedures) auch auf andere Arten einschränken, z.B.
module mod_bsp implicit none save private :: x ! , .... real :: x = 1.2, y = 9.8 ! ...
oder
module mod_bsp implicit none save private public :: y, addX ! , ... real :: x = 1.2, y = 9.8 ! ...
Datenkapselung, COMMONS
-Ersatz: Module als DatenbereichBearbeiten
Module als reinen Datenbereich zu nutzen ist vor allem dann interessant, wenn aus mehreren Programmeinheiten auf die gleichen Daten zugegriffen werden muss, jedoch die zugreifenden Unterprogramme nicht wirklich modulspezifisch sind und dementsprechend nicht als Bestandteil des Moduls angelegt werden.
Beispiel:
Fortran 90/95-Code (free source form) |
module konstanten real, parameter :: PI = 3.141593 real, parameter :: E = 2.718282 end module konstanten ! Hauptprogramm program bsp use konstanten implicit none real :: kreisflaeche write(*,*) 'PI = ', PI write(*,*) 'E = ', E write(*,*) 'Kreisflaeche fuer r=2.1 = ', kreisflaeche(2.1) call calcPiMalE end program bsp ! Unterprogramm 1 real function kreisflaeche(r) use konstanten implicit none real, intent (in) :: r kreisflaeche = r**2 * PI end function kreisflaeche ! Unterprogramm 2 subroutine calcPiMalE() use konstanten implicit none write (*,*) 'PI * E = ', PI * E end subroutine calcPiMalE ! Ausgabe: ! PI = 3.141593 ! E = 2.718282 ! Kreisflaeche fuer r=2.1 = 13.85442 ! PI * E = 8.539735 |
Datenabstraktion: Zusammenfassung von Daten und Methoden in einem ModulBearbeiten
Sind Daten und Unterprogramme als zusammengehörend zu betrachten, so ist es sinnvoll diese auch gemeinsam in einem Modul abzulegen. Ein Vorteil dabei ist, dass somit die Zugriffssteuerung gezielt eingesetzt werden kann. Moduldaten, die nicht von außerhalb des Moduls geändert werden dürfen, werden als private
deklariert. Der Zugriff auf diese Daten kann dann nur noch mittels Methoden des Moduls erfolgen.
Beispiel:
Fortran 90/95-Code (free source form) |
module kreis implicit none save real, parameter :: PI = 3.141593 real, private :: r = 0.0 contains subroutine setR(val) real, intent(in) :: val r = val end subroutine setR real function getR() getR = r end function getR real function kreisflaeche() kreisflaeche = r**2 * PI end function kreisflaeche end module kreis ! Hauptprogramm program bsp use kreis implicit none call setR(5.0) write (*,*) "r = ", getR() write (*,*) "Flaeche = ", kreisflaeche() call sub1(5.0) end program bsp ! Unterprogramm subroutine sub1(val) use kreis implicit none real, intent(in) :: val call setR(val*2.56) write (*,*) "r = ", getR() write (*,*) "Flaeche = ", kreisflaeche() end subroutine sub1 ! Ausgabe: ! r = 5. ! Flaeche = 78.539825 ! r = 12.799999 ! Flaeche = 514.7185 |
Modul und DatenverbundBearbeiten
Das Schlüsselwort private
darf in einem Datenverbund nur in Verbindung mit einem Modul Anwendung finden. Einerseits kann die Sichtbarkeit aller Variablen im Datenverbund eingeschränkt werden. Anderseits kann auch der Datenverbund selbst als private
deklariert werden. Ein Zugriff auf derart deklarierte Datenelemente ist dann nur noch durch Unterprogramme des gleichen Moduls möglich. Standardmäßig sind sowohl Datenverbund als auch seine Einzelkomponenten, so wie alle anderen nicht zugriffsbeschränkten Datenelemente, in einem Modul öffentlich (public).
Beispiel: Öffentlicher Datenverbund
Fortran 90/95-Code (free source form) |
module mod1 save type :: tripel real:: x, y, z end type tripel end module mod1 program bsp use mod1 implicit none type(tripel) :: tr = tripel(10.5, 0.0, -6.5) write(*, *) tr ! Ausgabe: ! 10.5 0.0 -6.5 end program bsp |
Beispiel: Öffentlicher Datenverbund mit privaten Datenelementen
Fortran 90/95-Code (free source form) |
module mod1 save type :: tripel private real:: x, y, z end type tripel contains subroutine createTripel(this, valX, valY, valZ) type(tripel) :: this real, intent(in) :: valX, valY, valZ this%x = valX this%y = valY this%z = valZ end subroutine createTripel subroutine writeTripel(this) type(tripel) :: this write(*,*) this end subroutine writeTripel end module mod1 program bsp use mod1 implicit none type(tripel) :: tr call createTripel(tr, 10.5, 0.0, -6.5) call writeTripel(tr) ! Ausgabe: ! 10.50000 0.000000 -6.500000 end program bsp |
Beispiel: Privater Datenverbund
Fortran 90/95-Code (free source form) |
module mod1 save type, private :: tripel real :: x = 0.0, y=0.0, z=0.0 end type tripel type(tripel), private :: t contains subroutine changeTripel(valX, valY, valZ) real, intent(in) :: valX, valY, valZ t = tripel(valX, valY, valZ) end subroutine changeTripel subroutine writeTripel() write (*,*) t end subroutine writeTripel end module mod1 program bsp use mod1 implicit none ! Hier könnte z.B. keine Variable vom Typ "triple" angelegt werden, da in dieser PE nicht ! sichtbar (privater Datenverbund des Moduls mod1) call writeTripel ! Ausgabe: ! 0.0 0.0 0.0 call changeTripel(10.5, -5.0, -3.5) call writeTripel ! Ausgabe: ! 10.5 -5. -3.5 call unterprogramm ! Ausgabe: ! 10.5 -5. -3.5 end program bsp subroutine unterprogramm use mod1 call writeTripel end subroutine unterprogramm |
- Dieses Beispiel scheint auf den ersten Blick im Gegensatz zu den beiden vorherigen Varianten unnötige Einschränkungen aufzuweisen. Im Haupt- und Unterprogramm können keine Variablen des Typs
tripel
angelegt werden. Im Modul wird immer auf die gleiche Variablet
zugegriffen. Das gewählte Beispiel könnte somit als Singleton beschrieben werden. Es ist außerhalb des Moduls sichergestellt, dass immer nur ein Tripel existiert. Singletons werden auch in der objektorientierten Softwareentwicklung verwendet. Daneben sind auch andere Situationen vorstellbar, in denen sich das Konzept eines privaten Datenverbunds als nützlich erweisen kann.
Die Schnittstelle: Das Modul als UnterprogrammbibliothekBearbeiten
Ein Modul kann als Sammelstelle für Unterprogramme dienen - quasi eine Bibliothek für häufig und in verschiedenen Kontexten benötigte Prozeduren.
Vorteile:
- Ordnung
- Modul kann in eine eigenständige Datei ausgelagert werden.
- Parameter-Datentypüberprüfung.
Beispiel: Parameter-Datentypüberprüfung
ohne Modul | mit Modul |
---|---|
program bsp implicit none integer :: zahl = 5 call unterprogramm(zahl) end program bsp subroutine unterprogramm(a) implicit none real, intent(in) :: a write (*,*) a end subroutine unterprogramm |
module test contains subroutine unterprogramm(a) implicit none real, intent(in) :: a write (*,*) a end subroutine unterprogramm end module test program bsp use test implicit none integer :: zahl = 5 call unterprogramm(zahl) end program bsp |
Die Datenausgabe zur Programmlaufzeit liefert jeweils einen fehlerhaften Wert. |
Bereits beim Compilieren tritt eine Fehlermeldung auf, z.B. bei g95: In file ....f90:20 call unterprogramm(zahl) 1 Error: Type mismatch in parameter 'a' at (1). Passing INTEGER(4) to REAL(4) Das Programm lässt sich so nicht compilieren. |
Der Schnittstellenblock: interface
Bearbeiten
Fortran 90/95 kennt das Sprachkonstrukt interface
, welches sich in vielerlei Hinsicht nutzbringend verwenden läßt. Schnittstellenblöcke sind modulunabhängig verwendbar.
interface [name] [interface-Spezifikationsteil] end interface [name] |
Der interface-Spezifikationsteil beinhaltet nur diejenigen Informationen, die für die Deklaration der Schnittstelle relevant sind (z.B. Unterprogrammbezeichnung, Variablendeklaration). Die genaue Festlegung der Unterprogramme (inkl. Ausführungsteil) erfolgt dann außerhalb des Schnittstellenblocks. Ein wesentliches Merkmal des Schnittstellenblocks ist die penible Überprüfung der Unterprogrammparameterdatentypen, so wie das auch beim Einsatz von Modulen geschieht.
Beispiel: Parameter-Datentypüberprüfung
ohne Schnittstellenblock | mit Schnittstellenblock |
---|---|
program bsp implicit none integer :: zahl = 5 call unterprogramm(zahl) end program bsp subroutine unterprogramm(a) implicit none real, intent(in) :: a write (*,*) a end subroutine unterprogramm |
program bsp implicit none integer :: zahl = 5 interface subroutine unterprogramm(a) real, intent(in) :: a end subroutine unterprogramm end interface call unterprogramm(zahl) end program bsp subroutine unterprogramm(a) implicit none real, intent(in) :: a write (*,*) a end subroutine unterprogramm |
Die Datenausgabe zur Programmlaufzeit liefert jeweils einen fehlerhaften Wert. |
Bereits beim Compilieren tritt eine Fehlermeldung auf, z.B. bei gfortran: In file ....f90:15 call unterprogramm(zahl) 1 Error: Type/rank mismatch in argument 'a' at (1) Das Programm lässt sich so nicht compilieren. |
Generische UnterprogrammschnittstelleBearbeiten
Fortran 90/95 ermöglicht generische Methoden. Die im Schnittstellenblock deklarierten Unterprogramme können damit über den gleichen Unterprogrammnamen aufgerufen werden. Intern erfolgt dann der Aufruf des jeweils passenden Unterprogramms durch die Unterschiede der Datentypen der Unterprogrammparameter. Mittel zum Zweck ist der benannte Schnittstellenblock.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none interface gensub subroutine writeReal(val) real, intent(in) ::val end subroutine writeReal subroutine writeInteger(val) integer, intent(in) ::val end subroutine writeInteger subroutine writeCharacter(val) character, intent(in) :: val end subroutine writeCharacter end interface gensub call gensub(5.5) call gensub(3) call gensub("H") call writeCharacter("X") ! Ausgabe: ! Real-Wert = 5.500000 ! Integer-Wert = 3 ! Zeichen = H ! Zeichen = X end program bsp subroutine writeReal(val) real, intent(in) ::val write (*,*) "Real-Wert = ", val end subroutine writeReal subroutine writeInteger(val) integer, intent(in) ::val write (*,*) "Integer-Wert = ", val end subroutine writeInteger subroutine writeCharacter(val) character, intent(in) ::val write (*,*) "Zeichen = ", val end subroutine writeCharacter |
OperatorüberladungBearbeiten
Fortran 90/95 verwendet von Haus aus Operatorüberladung. So können z.B. Variablen arithmetischen Datentyps einfach miteinander addiert werden:
c = a + b
Dieser Ausdruck funktioniert unabhängig davon, ob die Variablen vom Typ Ganzzahl oder Gleitkommzahl sind. Und auch für Felder funktioniert diese einfache Form der Addition. Die einzelnen Feldkomponenten werden korrekt addiert. Das ist in anderen Programmiersprachen nicht selbstverständlich. Zusätzlich kann in Fortran 90/95 auch der Programmierer mit Hilfe von Schnittstellenblöcken sogenannte defined operations festlegen.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none interface operator (.PLUS.) function charAdd(c1, c2) character, intent(in) :: c1, c2 character(len=2) :: charAdd end function charAdd end interface operator (.PLUS.) ! "Addition" mittels definiertem .PLUS.-Operator write (*,*) "c1 .PLUS. c2 = ", "A" .PLUS. "B" ! oder auch mittels Funktion write (*,*) "charAdd () = ", charAdd("A", "B") ! Ausgabe: ! c1 .PLUS. c2 = AB ! charAdd () = AB end program bsp function charAdd(c1, c2) implicit none character, intent(in) :: c1, c2 character(len=2) :: charAdd charAdd = c1 // c2 end function charAdd |
Beispiel: nur mit gfortran compilierbar, nicht mit g95 oder ifort
Fortran 90/95-Code (free source form) |
module mod type :: tripel real x, y, z end type end module mod program bsp use mod implicit none interface operator (*) function tripelMult(t1, t2) type(tripel), intent(in) :: t1, t2 type(tripel) :: tripelMult end function tripelMult end interface operator (*) ! Multiplikation mittels überladenem *-Operator write (*,*) "t1 * t2 =", tripel(2.0, 3.0, 4.0) * tripel(1.5, 0.5, 2.0) ! oder auch mittels Funktion write (*,*) "tripelMult () =", tripelMult(tripel(2.0, 3.0, 4.0), tripel(1.5, 0.5, 2.0)) ! Ausgabe: ! t1 * t2 = 3.000000 1.500000 8.000000 ! tripelMult () = 3.000000 1.500000 8.000000 end program bsp function tripelMult(t1, t2) use mod implicit none type(tripel), intent(in) :: t1, t2 type(tripel) :: tripelMult tripelMult = tripel(t1%x * t2%x, t1%y*t2%y, t1%z*t2%z) end function tripelMult |
Bei der Verwendung von defined operations sind jedoch einige Bedingungen zu beachten.
Neben den defined operations (interface operator
) gibt es auch noch defined assignments (interface assignment
) für die Überladung des Zuweisungsoperators.
Schnittstellenblock und ModulBearbeiten
Beim vorigen Beispiel bestand das Problem, dass nur einer der getesteten Compiler eine ausführbare Datei zu Stande brachte. Abhilfe schaffen kann die Verlagerung des Schnittstellenblocks in das Modul unter Zuhilfenahme von module procedure
.
Beispiel: Diesen Programmcode schlucken sowohl gfortran, g95 als auch ifort problemlos
Fortran 90/95-Code (free source form) |
module mod type :: tripel real x, y, z end type interface operator (*) module procedure tripelMult end interface operator (*) contains function tripelMult(t1, t2) implicit none type(tripel), intent(in) :: t1, t2 type(tripel) :: tripelMult tripelMult = tripel(t1%x * t2%x, t1%y*t2%y, t1%z*t2%z) end function tripelMult end module mod program bsp use mod implicit none ! Multiplikation mittels überladenem *-Operator write (*,*) "t1 * t2 =", tripel(2.0, 3.0, 4.0) * tripel(1.5, 0.5, 2.0) ! oder auch mittels Funktion write (*,*) "tripelMult () =", tripelMult(tripel(2.0, 3.0, 4.0), tripel(1.5, 0.5, 2.0)) ! Ausgabe: ! t1 * t2 = 3.000000 1.500000 8.000000 ! tripelMult () = 3.000000 1.500000 8.000000 end program bsp |
Unterprogramme: Die Übergabe optionaler und benannter ParameterBearbeiten
Der Einsatz optionaler Parameter wurde bereits im Kapitel Unterprogramme abgehandelt. Zusätzlich unterstützt Fortran 90/95 auch die Verwendung von benannten Parametern (argument keywords). Dazu ist es erforderlich, dass das Unterprogramm ein explizites Interface aufweist, wie das z.B. bei Einbindung von Unterprogrammen in Module automatisch der Fall ist.
Beispiel:
Fortran 90/95-Code (free source form) |
module m1 contains subroutine abc( var1, var2 ) implicit none integer, intent( in ) :: var1, var2 write( *, * ) var1, var2 end subroutine abc end module m1 program bsp use m1 implicit none call abc( 12, 99 ) call abc( var2 = 99, var1 = 12 ) call abc( var2 = 12, var1 = 99 ) call abc( 12, var2 = 99 ) ! call abc( var1 = 12, 99 ) ! so funktioniert das nicht ! Ausgabe: ! 12 99 ! 12 99 ! 99 12 ! 12 99 end program bsp |
SonstigesBearbeiten
use modulbezeichnung, only : modulelemente
use modulbezeichnung => bezeichnung
Objektorientierte ProgrammierungBearbeiten
Modulares Programmieren reißt heutzutage niemanden mehr vom Hocker und selbst objektorientierte Konzepte sind seit spätestens Ende der 80-Jahre des vergangenen Jahrhunderts Stand der Technik. Im akademischen Bereich waren diese Dinge natürlich schon früher bekannt. Fortran hat in diesem Bereich also nie eine Vorreiterrolle inne gehabt. Nach jeweils kurzen Nachdenkphasen haben aber die Fortran-Leute unerschrocken auch in diesem Bereich nachgezogen.
Fortran bildet also aufgrund seiner langen Geschichte ein weites Spektrum der Programmierparadigmen ab: vom prozeduralen Programmieren (FORTRAN 77) über das modulare Programmieren (Fortran 90/95) hin zum objektorientierten Programmieren (Fortran 2003).
Fortran 90/95 ist keine typisch objektorientierte Sprache. Trotzdem lässt sich mit den modularen Spracheigenschaften von Fortran 90/95 bereits in weiten Bereichen Objektorientierung simulieren beziehungsweise nachbauen. Aber erst mit Fortran 2003 lässt sich wirklich komfortabel objektorientiert programmieren. Diese Fortran-Version wirbt dann auch „offiziell“ mit diesem Merkmal.
Welche Eigenschaften verbindet man typischerweise mit Objektorientierung? Da wären
- Klassen - Datenkapselung und Datenabstraktion
- Objekte
- Vererbung
- Statische Polymorphie: Überladen von Funktionen (und Operatoren)
- Run-Time-Polymorphie
Welche dieser Merkmale bietet Fortran 90/95 von Haus aus und was läßt sich relativ einfach nachbauen?
Klassen - Datenkapselung und DatenabstraktionBearbeiten
Was ist eine Klasse?
Ein Fortran 90/95-Modul sieht sehr ähnlich aus wie eine Klasse in der objektorientierten Programmierung und ist dementsprechend auch Ausgangspunkt für den objektorientierten Ansatz in Fortran 90/95.
Klasse | Fortran 90/95 | |||
---|---|---|---|---|
|
module ... ! Datenbereich contains ! Methodenbereich end module ... |
Einen grundlegenden Mechanismus zum Schreiben objektorientierter Programme stellt Fortran 90/95 somit in Form der Module zur Verfügung. Jetzt stellt sich aber die Frage, wie diese Klasse instantiiert werden soll. Das wird im Abschnitt Objekte gezeigt.
ObjekteBearbeiten
Was sind Objekte? Objekt (Programmierung)
Ein einfaches Modul mit ein paar konventionellen Datenelementen und Prozeduren ist noch keine Klasse, aus der Objekte zu erzeugen wären. Dazu bedarf es noch eines Unterscheidungskriteriums zwischen den einzelnen Objekten. Dieses Unterscheidungskriterium kann, wie auch schon früher im Teilkapitel Modul und Datenverbund gezeigt, mittels Datenverbund hergestellt werden. Die Objekte werden somit nicht über den Modulnamen als Klasse angesprochen, sondern über die Datenverbund-Bezeichnung. Auch das ist keine neue Erkenntnis, sondern eine geschickte Ausnutzung eines Mechanismus, den Fortran 90/95 standardmäßig mit Modulen und Datenverbund zur Verfügung stellt.
Beispiel:
Fortran 90/95-Code (free source form) |
module tripelClass implicit none save type :: tripel real:: x, y, z end type tripel contains subroutine write(this) type(tripel), intent(in) :: this write(*, *) "********** Tripel **********" write(*, *) this end subroutine end module tripelClass program bsp use tripelClass implicit none type(tripel) :: obj1 = tripel(1.5, 0.0, -6.5) type(tripel) :: obj2 = tripel(2.5, 1.0, -4.5) type(tripel) :: obj3 = tripel(3.5, 2.0, -2.5) call write(obj1) call write(obj2) call write(obj3) ! Ausgabe: ! ********** Tripel ********** ! 1.500000 0.000000 -6.500000 ! ********** Tripel ********** ! 2.500000 1.000000 -4.500000 ! ********** Tripel ********** ! 3.500000 2.000000 -2.500000 end program bsp |
Weitere wichtige Elemente beim Erzeugen und Zerstören von Objekten sind in der OOP die sogenannten Konstruktoren und Destruktoren.
Was sind Konstruktoren und Destruktoren? Konstruktoren und Destruktoren
Konstruktoren und Destruktoren kennt Fortran 90/95 natürlich nicht. Im Bedarfsfall sind diese Elemente also mittels konventioneller Unterprogramme nachzubilden und dann jeweils explizit in den entsprechenden Programmabschnitten, nach Erzeugung beziehungsweise beim Abbau des jeweiligen Objektes, manuell aufzurufen.
VererbungBearbeiten
Was ist Vererbung? Vererbung (Programmierung)
Das OOP-Prinzip „Vererbung“ ist in Fortran 90/95 nur über Umwege realisierbar. Fortran 90/95 kennt konzeptionsbedingt für diesen Zweck keinen einfachen programmtechnischen Mechanismus. Nachfolgend wird anhand eines kleinen und überschaubaren Beispiels eine mögliche Lösung demonstriert.
Beispiel:
Fortran 90/95-Code (free source form) |
module tripelClass implicit none save type :: tripel real:: x, y, z end type tripel contains subroutine write(this) type(tripel), intent(in) :: this write(*, *) "********** Tripel **********" write(*, *) this end subroutine end module tripelClass module coordClass use tripelClass implicit none save type :: coord type(tripel) :: tr integer :: id end type contains subroutine constructCoord(this, t, i) implicit none type(coord), intent(out) :: this type(tripel), intent(in) :: t integer :: i this%tr = t this%id = i end subroutine constructCoord subroutine writeCoord(this) implicit none type(coord), intent(in) :: this write(*, "(A, I5, A)") "******************* KOORDINATE ", this%id, " *******************" call write(this%tr) end subroutine writeCoord end module coordClass program bsp use coordClass implicit none type(coord) :: obj1 = coord(tripel(1.5, 0.0, -6.5), 1005) type(coord) :: obj2 = coord(tripel(2.5, 1.0, -4.5), 1006) type(coord) :: obj3 = coord(tripel(3.5, 2.0, -2.5), 1007) call writeCoord(obj1) call writeCoord(obj2) call writeCoord(obj3) ! Ausgabe: ! ******************* KOORDINATE 1005 ******************* ! ********** Tripel ********** ! 1.500000 0.000000 -6.500000 ! ******************* KOORDINATE 1006 ******************* ! ********** Tripel ********** ! 2.500000 1.000000 -4.500000 ! ******************* KOORDINATE 1007 ******************* ! ********** Tripel ********** ! 3.500000 2.000000 -2.500000 end program bsp |
Statische Polymorphie: Überladen von Funktionen (und Operatoren)Bearbeiten
Das Überladen von Funktionen und Operatoren unterstützt Fortran 90/95 wiederum standardmäßig. Diese Konzepte wurden schon in den Abschnitten
kurz erläutert.
Run-Time-PolymorphieBearbeiten
Polymorphie zur Laufzeit eines Programmes ist mit den Mitteln von Fortran 90/95 nur einigermaßen aufwendig und kompliziert nachzubauen, jedoch prinzipiell möglich. Es sei zu diesem Thema auf die weiterführende Literatur verwiesen.
AusblickBearbeiten
Sind die Methoden zur Nachbildung objektorientierter Mechanismen auch in Fortan 2003 verwendbar? Prinzipiell schon, jedoch bietet Fortran 2003 bessere und erweiterte Möglichkeiten zur Behandlung dieses Themas. Dort wurde nämlich das Prinzip des Datenverbunds wesentlich erweitert. Ein type
-Block kann dann neben den Datenelementen auch Unterprogramme beinhalten, das Schlüsselwort extends
zwecks Vererbungmechanismus ist vorhanden, etc. Das in Fortran 90/95 als Datenbund/Struktur verwendbare type
-Konstrukt wurde also in Fortran 2003 zu einer Klasse, ähnlich wie es in anderen Programmierspachen unter der Bezeichnung class
bekannt ist, aufgerüstet.
LiteraturBearbeiten
QuellenBearbeiten
Weiterführende LiteraturBearbeiten
- Decyk, Norton, Szymanski: Introduction to Object-Oriented Concepts Using Fortran90, 1996
- Decyk, Norton, Szymanski: How to Express C++ Concepts in Fortran90. 1997
- Decyk, Norton, Szymanski: How to Support Inheritance and Run-Time Polymorphism in Fortran90, 1998
- Decyk, Norton: A Simplified Method for Implementing Run-Time Polymorphism in Fortran95, 2004
- Decyk, Gardner: Object-Oriented Design Patterns in Fortran95, 2006
Offizielle Fortran 95-ErweiterungenBearbeiten
Hier sind jene ISO/IEC-Standards angeführt, die nicht Teil des Original-Fortran 95-Standards sind, sondern erst später als offizielle Fortran 95-Erweiterungen formuliert wurden. Etliche Fortran 95-Compiler bieten diese Features derzeit noch nicht oder nur teilweise.
Zeichenketten variabler LängeBearbeiten
- Offizielle Bezeichnung: Fortran, Part 2, Varying length character strings
- Standards:
- Referenzmodul-Download-Adressen:
- Sample_Module iso_vst.f95 (pointer-Variante) - programmiert in Standard-Fortran 95 und daher portabel
- Sample_Module iso_vsta.f95 (allocatable-Variante) - verwendet Sprachmittel gem. Fortran 95-Erweiterung ISO/IEC TR 15581 : 1999 bzw. 2001 (enhanced data type facilities). Dieses Modul soll effizienter als iso_vst.f95 sein und Speicherlecks vermeiden
Diese Fortran 95-Erweiterung ermöglicht in einfacher Weise das Arbeiten mit Zeichenketten variabler Länge. Die notwendigen Datenelemente und Funktionen sind im Modul iso_varying_string
hinterlegt. Die nötigen Programmabläufe zur dynamische Anpassung des jeweils erforderlichen Speicherplatzes übernehmen somit die Unterprogramme dieses Moduls. Eine explizite Begrenzung der maximalen Zeichenkettenlänge ist nicht vorgesehen. Einschränkungen ergeben sich nur durch Hardwarerestriktionen bzw. die Programmkomplexität.
In den getesteten aktuellen Fortran 95-Compilern (gfortran, g95 und ifort) ist mit Stand 01.01.2007 diese Funktionalität noch nicht inkludiert. Zur Nutzung dieses Features mit diesen Compilern ist deshalb der Download und die Einbindung einer der o.a. Moduldateien erforderlich (oder man schreibt selbst ein entsprechendes standardkonformes Modul).
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp use iso_varying_string implicit none type( varying_string ) :: str str = "Wiki" write (*,*) len(str) write (*,*) char(str) str = str // "books" write (*,*) len(str) write (*,*) char(str) ! Ausgabe ! 4 ! Wiki ! 9 ! Wikibooks end program bsp |
Compilieren, Linken:
gfortran -o bsp iso_vsta.f95 bsp.f90
Prinzipiell gelten für Zeichenketten mit variabler Länge die gleichen generisch-intrinsischen Funktionsbezeichner wie für normale Zeichenketten. Auch Stringkonkatenation, Zuweisung und Vergleichsoperationen sind wie gewohnt erlaubt. Zuätzlich kommen noch einige neue Funktionen hinzu:
Funktionsbezeichner | Kommentar |
---|---|
var_str | Datentypkonvertierung. character → varying_string
|
get, put, put_line | I/O |
insert | Einfügen eines Teilstrings |
replace | Ersetzen eines Teilstrings |
remove | Entfernen eines Teilstrings |
extract | Extrahierung eines Teilstrings |
split | Einen String in zwei Strings splitten. |
Bedingte CompilierungBearbeiten
Mannigfaltig sind die Gründe, welche den Einsatz bedingter Compilierung (conditional compilation, Kurzform: coco) erstrebenswert erscheinen lassen. Seien es die unterschiedlichen Betriebssystemkonventionen zur Vergabe von Pfad- und Dateinamen, unterschiedliche Anforderungen an Entwickler- und Releaseversionen eines Programmes oder auch spezielle internationale Marktbedürfnisse, die Programmvarianten erfordern. All diese Bedürfnisse und noch viel mehr kann coco befriedigen.
Weder g95, noch gfortran oder ifort bieten bisher von Haus aus die Möglichkeit zur "conditional compilation". Es gibt aber externe Tools zur ISO/IEC 1539-3, die als Präprozessor fungieren können und somit das gewünschte Verhalten erzeugen, z.B. das Programm coco.
Beispiel:
Die Datei bsp.fpp:
?? integer, parameter :: varianteA = 1, varianteB = 2 ?? integer :: flag = 1 program bsp ?? if ( flag == varianteA ) then write (*,*) "Variante A" ?? else if (flag == varianteB) then write (*,*) "Variante B" ?? endif end program bsp
mit der Set-Datei bsp.set:
?? integer :: flag = 2 ?? alter: delete
ergibt nach einem Präprozessorlauf coco bsp
folgendes Fortran-Programm bsp.f90
program bsp write (*,*) "Variante B" end program bsp
Beispiel:
Wird die coco-Anweisung alter: delete
in der Set-Datei weggelassen, so werden die überflüssigen Zeilen nicht gelöscht, sondern nur auskommentiert (shift). Standardmäßig entspricht dies beim coco-Präprozessor einer alter: shift3
-Set-Anweisung (!?>). Die Set-Datei bsp.set
?? integer :: flag = 2
würde mit der obigen bsp.fpp-Datei nach einem coco bsp
also diesen Fortran-Code liefern
!?>?? integer, parameter :: varianteA = 1, varianteB = 2 !?>?? integer :: flag = 1 program bsp !?>?? if ( flag == varianteA ) then !?> write (*,*) "Variante A" !?>?? else if (flag == varianteB) then write (*,*) "Variante B" !?>?? endif end program bsp !?>?? This was produced using the following SET file !?>?? integer :: flag = 2
Floating-point ExceptionsBearbeiten
Im Fortran 2003-Standard sind Module betreffend IEEE 754-Standard (IEEE Standard for Binary Floating-Point Arithmetic) enthalten. Näheres dazu wird im Fortran 2003-Kapitelabschnitt Die intrinsischen IEEE-Module beschrieben.
Allocatable ComponentsBearbeiten
Diese Erweiterung bezieht sich auf das Schlüsselwort allocatable
. In Standard-Fortran 90/95 ist die dynamische Allokation von Speicherplatz für Felder mit dem Attribut allocatable
eigentlich nur in einem lokalen Kontext möglich. Der Technical Report 15581 fordert, dass solche Felder darüber hinaus auch in den Anwendungsbereichen
- Rückgabewert von Funktionen
- Unterprogrammparameter
- Feldelemente in Datenverbunden
uneingeschränkt verwendbar sein sollen.
Die Enhanced Data Type Facilities werden bereits standardmäßig vom aktuellen g95-, gfortran-, ifort- und Sun-Fortran-Compiler unterstützt.
Beispiel: Rückgabewert
Fortran 90/95-Code (free source form) |
program bsp implicit none write ( *, * ) fun_all( 5 ) write ( *, * ) fun_all( 7 ) ! Ausgabe: ! 1 2 3 4 5 ! 1 2 3 4 5 6 7 contains function fun_all( n ) implicit none integer, dimension( : ), allocatable :: fun_all integer, intent( in ) :: n integer :: j, st allocate( fun_all( n ), stat = st ) if( st == 0 ) then forall( j = 1 : n ) fun_all(j) = j end forall else write( *, * ) "Allocate-Fehler" end if end function fun_all end program bsp |
Beispiel: Unterprogrammparameter
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension( : ), allocatable :: dynarr integer :: j allocate( dynarr( 3 ) ) forall( j = 1 : 3 ) dynarr( j ) = j * 2 end forall call fun_all( dynarr ) write( *, * ) "Out: ", dynarr deallocate( dynarr ) allocate( dynarr( 5 ) ) forall( j = 1 : 5 ) dynarr( j ) = j * 3 end forall call fun_all( dynarr ) deallocate( dynarr ) ! Ausgabe: ! Argument: 2 4 6 ! (De)Allocate im UP: 88 99 ! Out: 88 99 ! Argument: 3 6 9 12 15 ! (De)Allocate im UP: 88 99 contains subroutine fun_all( a ) implicit none integer, dimension( : ), allocatable, intent( inout ) :: a write( *, * ) "Argument:", a deallocate( a ) allocate( a (2) ) a(1) = 88 a(2) = 99 write( *, * ) "(De)Allocate im UP:", a end subroutine fun_all end program bsp |
Mit Standard-Fortran 90/95 könnten sie zwar das allozierte Feld als Parameter aus dem Hauptprogramm an das Unterprogramm übergeben, dort wäre es aber nicht als allocatable
kennzeichenbar und somit im Unterprogramm nicht in der gezeigten Art und Weise (de)allozierbar. Mittels Standardkonformitäts-Compileroptionen, z.B.
- Intel Fortran Compiler:
-stand
- g95, gfortran:
-std=f95
ist überprüfbar, welche Möglichkeiten Standard-Fortran 95 bietet und welche Eigenschaften den Erweiterungen zuschreibbar sind.
Beispiel: allocatable array im Datenverbund
Fortran 90/95-Code (free source form) |
program bsp implicit none type struktur integer :: nr integer, dimension( : ), allocatable :: arr end type struktur type( struktur ) :: a1, a2 allocate( a1%arr( 5 ) ) allocate( a2%arr( 2 ) ) a1%nr = 9453 a1%arr(1) = 1 a1%arr(5) = 5 a2%nr = 9454 a2%arr(1) = 11 a2%arr(2) = 22 write ( *, * ) "a1 =" , a1%nr, a1%arr write ( *, * ) "a2 =", a2%nr, a2%arr ! Ausgabe: ! a1 = 9453 1 0 0 0 5 ! a2 = 9454 11 22 end program bsp |
ProgrammaufbauBearbeiten
Programmaufbau und ZeilenformatBearbeiten
Der grundlegende Programmaufbau und das Zeilenformat von Fortran 90/95 wurden in Fortran 2003 beibehalten. Neben der free source form ist aus Kompatibilitätsgründen auch noch immer die alte, aus FORTRAN 77 bekannte, fixed source form gültig.
Eine Zeile darf auch in Fortran 2003 bei Verwendung der free source form standardmäßig maximal 132 Zeichen beinhalten. Ein symbolischer Name darf nun höchstens 63 Zeichen lang sein. Eine Anweisung darf sich maximal über 256 Zeilen erstrecken. Das Zeilenfortsetzungszeichen ist wie in Fortran 90/95 das Kaufmanns-Und: &
.
ZeichenvorratBearbeiten
Der Fortran 2003-Zeichenvorrat wurde gegenüber Fortran 90/95 erweitert:
Großbuchstaben: | A bis Z |
Kleinbuchstaben: | a bis z |
Ziffern: | 0 bis 9 |
FORTRAN 77-Sonderzeichen: | + - * / = ( ) : , . ' $ und Leerzeichen |
Fortran 90/95-Sonderzeichen: | _ ! ? " & ; < > |
Fortran 2003-Sonderzeichen: | \ [ ] { } ~ ` ^ | # @ |
|
Anwendungsgebiet der neu hinzugekommenen ZeichenBearbeiten
Von den in Fortran 2003 neu zugefügten Zeichen haben nur die eckigen Klammern einen konkreten Anwendungsbereich als Kennzeichnung von Feldkonstruktoren. Ein Feldkonstruktor darf neben der aus Fortran 95 bekannten Form
(/ werte /)
nun auch mit eckigen Klammern geschrieben werden
[ werte ]
Beispiel:
Fortran 2003-Code |
! bsp.f03 program bsp implicit none integer, dimension( 3 ) :: a = [ 20, 33, 55 ] integer, dimension( 2 ) :: b = (/ 44, 55 /) write( *, *) a write( *, *) b ! Ausgabe: ! 20 33 55 ! 44 55 end program bsp |
Kompilieren, linken:
g95 -o bsp bsp.f03
DatentypenBearbeiten
Intrinsische DatentypenBearbeiten
Die aus Fortran 90/95 bekannten intrinsischen Datentypen sind weiterhin uneingeschränkt gültig.
Datentyp | Kommentar |
---|---|
integer | Ganzzahlen |
real | Reelle Zahlen einfacher Genauigkeit |
(double precision) | Reelle Zahlen doppelter Genauigkeit |
complex | Komplexe Zahlen |
logical | Logischer Datentyp (.true., .false.) |
character | Zeichen(kette) |
Im Umfeld des character
-Datentyps gab es einige kleinere Ergänzungen:
- Unterstützung internationaler Zeichensätze in Rahmen der ISO 10646-Norm (Universal Coded Character Set)
- Die Funktion
selected_char_kind
ist neu. Ihre Funktionsweise ist äquivalent zu den bereits aus Fortran 90/95 bekannten Funktionenselected_int_kind
undselected_real_kind
.
EnumerationBearbeiten
Mit Fortran 2003 sind auch in dieser Programmiersprache Enumerationen (Aufzählungstypen) möglich. Die Werte in einer solchen Enumeration besitzen einen integer
-Datentyp.
enum, bind( C ) enumerator :: wert(e) // ... end enum |
Die von C-Enumerationen bekannten Eigenschaften gelten gleichermaßen für Fortran-Enumerationen, z.B.:
- ohne explizite Zuweisung von Werten wird mit dem Wert 0 gestartet.
- ohne explizite Zuweisung von Werten wird in der Anordnungsreihenfolge der Elemente sukzessiv immer um 1 hochgezählt.
- Wurde dem Vorgängerelement eine Ganzzahl zugewiesen, dem Element jedoch nicht, so ist der Wert dieses Elementes die dem Vorgängerelement zugeordnete Ganzzahl + 1.
Beispiel:
Fortran 2003-Code |
program bsp implicit none integer :: wert enum, bind( C ) enumerator :: MON, DIE, MIT = 3, DON, FRE = 5, SAM = 66, SON = 77 end enum write( *, * ) MON write( *, * ) MIT write( *, * ) SAM wert = 4 if( wert == MIT + 1 ) then write( *, * ) "Donnerstag" endif ! Ausgabe: ! 0 ! 3 ! 66 ! Donnerstag end program bsp |
Obwohl der Fortran 2003-Working Draft J3/04-007 durch das bind(c)
eine zwingende Anbindung an einen Aufzählungstyp in der Programmiersprache C vorgaukelt, ist dies nicht der Fall. Fortran-Enumerationen funktionierten auch ohne entsprechendes C-Gegenstück. Sie werden im Fortran 2003-Working Draft auch nicht im Abschnitt Interoperability with C geführt, sondern direkt im Abschnitt über die Fortran-Datentypen. Diese Tatsache dürften die Compilerbauer auch unterschiedlich interpretieren. So wäre beim g95-Compiler das Attribut bind(C)
nicht unbedingt erforderlich, ist jedoch möglich. Beim Einsatz des gfortran ist der Zusatz bind(C)
obligatorisch. Aus Kompatibilitätsgründen ist somit immer die strikte Schreibweise nach Fortran 2003-Standard empfehlenswert. Der Intel Fortran Compiler 9.1 unterstützt dieses Sprachmerkmal ohnehin noch nicht.
Derived TypeBearbeiten
Der Derived Type wurde in Fortran 2003 wesentlich erweitert. Er ist nun kein reiner Datenverbund wie noch in Fortran 90/95, sondern eine richtige Klasse. Genauer beschrieben wird dieser Typ später im Kapitel zur OOP, hier zunächst nur einige einfache Erweiterungsmerkmale gegenüber Fortran 90/95.
Benannte ParameterBearbeiten
Beispiel:
Fortran 2003-Code |
program bsp implicit none type test integer :: i character( 20) :: str end type type( test ) :: t1 = test ( i = 1, str = "Wiesenfeld" ) write( *, *) t1 ! Ausgabe: ! 1 Wiesenfeld end program bsp |
Die Verwendung von benannten Parametern bei der Konstruktion einer Derived Type-Variable kann vor allem in Verbindung mit Vorgabewerten sinnvoll sein.
VorgabewerteBearbeiten
Ab Fortran 2003 dürfen Variablen in einem Derived Type auch mit Vorgabewerten (default values) belegt werden.
Beispiel:
Fortran 2003-Code |
program bsp implicit none type test integer :: i = -1 character( 20) :: str = "NN" end type type( test ) :: t1 = test () type( test ) :: t2 = test ( str = "Wiesenfeld" ) type( test ) :: t3 = test ( str = "Walddorf", i = 1 ) write( *, *) t1 write( *, *) t2 write( *, *) t3 ! Ausgabe: ! -1 NN ! -1 Wiesenfeld ! 1 Walddorf end program bsp |
ZeigerBearbeiten
ProzedurenzeigerBearbeiten
EinführungBearbeiten
Mit dem 2003er-Standard unterstützt auch Fortran Zeiger auf Prozeduren (procedure pointer, Funktionszeiger). Diese spielen auch eine wichtige interne Rolle bei der Realisierung objektorientierter Mechanismen und beim C-Binding. Schematisch wird ein Prozedurenzeiger so deklariert:
procedure( [name] ), pointer :: zeigername |
Die Angabe von name
ist optional. Wird an dieser Stelle ein Name, z.B. ein Unterprogrammbezeichner angegeben, so bedeutet dies, dass der Prozedurenzeiger mit allen Unterprogrammen, die das gleiche Interface aufweisen, kompatibel ist. Prozedurenzeiger können wie normale Zeiger gehandhabt werden.
Beispiel:
Fortran 2003-Code |
program bsp implicit none procedure(up), pointer :: pptr => null() pptr => ooops call pptr ! Ausgabe: ! ooops contains subroutine ooops() write( *, * ) "ooops" end subroutine ooops subroutine up() end subroutine up end program bsp |
Prozedurenzeiger mit implizitem InterfaceBearbeiten
Bei der Deklaration eines Prozedurenzeigers muss keine explizite Schnittstelle angegeben werden. Im Folgenden wird dies am Beispiel eines mit pptr2
benannten Prozedurenzeigers demonstriert.
Beispiel:
Fortran 2003-Code |
program bsp implicit none procedure( up ) , pointer :: pptr1 => null() procedure( ) , pointer :: pptr2 => null() procedure( add ), pointer :: pptr3 => null() pptr1 => ooops pptr2 => ooops call pptr1 call pptr2 ! Ausgabe: ! ooops ! ooops pptr3 => add write( *, * ) pptr3( 5 , 12 ) ! Ausgabe: ! 17 ! Folgende auskommentierte Zuordnung waere nicht erlaubt: ! pptr1 => add contains subroutine up() end subroutine up subroutine ooops() write( *, * ) "ooops" end subroutine ooops function add( a, b ) integer :: add integer, intent( in ) :: a, b add = a + b end function add end program bsp |
Abstraktes InterfaceBearbeiten
Das bei der Deklaration eines Prozedurenzeigers als Schnittstelle genannte Unterprogramm muss nicht real implementiert sein. Es können statt dessen auch abstrakte Interfaces Verwendung finden. Diese sind gleich wie konventionelle Interface-Blöcke aufgebaut, mit dem Unterschied, dass sie als abstract interface
gekennzeichnet sind. Ein mit einem abstrakten Interface deklarierter Prozedurzeiger passt dann für jedes Unterprogramm, welches mit identer Schnittstelle ausgestattet ist.
Fortran 2003-Code |
program bsp implicit none abstract interface function afunc( x, y ) integer :: afunc integer, intent( in ) :: x, y end function afunc end interface procedure( afunc ), pointer :: pptr1 => null() procedure( add ) , pointer :: pptr2 => null() pptr1 => add write( *, * ) pptr1( 5 , 12 ) ! Ausgabe: 17 pptr1 => mult write( *, * ) pptr1( 3 , 2 ) ! Ausgabe: 6 ! Folgendes funktioniert uebrigens auch, da add() und mult() das gleiche Interface ! aufweisen: pptr2 => mult write( *, * ) pptr2( 5 , 5 ) ! Ausgabe: 25 contains function add( a, b ) integer :: add integer, intent( in ) :: a, b add = a + b end function add function mult( a, b ) integer :: mult integer, intent( in ) :: a, b mult = a * b end function mult end program bsp |
Zeiger und das intent
-AttributBearbeiten
Nun ist bei der Übergabe von Zeigern an Unterprogramme auch die Angabe eines intent
-Attributs möglich. Das war mit Fortran 90/95 noch nicht erlaubt. Diese intent
-Angaben beziehen sich aber nicht auf die Variablenwerte an sich, sondern beschränken nur die Möglichkeiten zur Zeigerzuordnung im Unterprogramm selbst.
Beispiel:
Fortran 2003-Code |
program bsp implicit none integer, target :: x = 15 integer, pointer :: ptr1 => null(), ptr2 => null() ptr1 => x ptr2 => x call mult( ptr1, ptr2) write( *, *) "Zuordnungsstatus ptr1:", associated( ptr1 ) write( *, *) "Zuordnungsstatus ptr2:", associated( ptr2 ) write( *, *) "Wert ptr1:", ptr1 write( *, *) "Wert x:", x ! Ausgabe: ! Zuordnungsstatus ptr1: T ! Zuordnungsstatus ptr2: F ! Wert ptr1: 45 ! Wert x: 45 contains subroutine mult( a, b ) integer, pointer, intent( in ) :: a integer, pointer, intent( inout ) :: b integer, target :: val = 3 ! Folgendes waere nun nicht erlaubt, da a nur intent( in ) ! a => null() ! Das auch nicht: ! a => val ! Das allerdings ist erlaubt: a = a * val ! b ist mit intent( inout ) spezifiziert, also ist hier eine Zeigerzuordnung ! erlaubt: b => null() end subroutine mult end program bsp |
Zeiger und FelderBearbeiten
Auch im Zusammenspiel von Zeigern mit Feldern bringt der Fortran 2003-Standard einige Ergänzungen.
Ein- und AusgabeBearbeiten
StreamsBearbeiten
Fortran 2003 bietet zusätzlich zum altbekannten datensatzbasierten I/O nun auch Dateieingabe und -ausgabe in Form von Streams, wie das z.B. in der Programmiersprache C seit jeher üblich ist.
Unterschied zum alten I/O-Konzept?Bearbeiten
- Streams ermöglichen das Lesen und Schreiben von Binärdateien, ohne sich mit den auf Datensätzen aufbauenden Strukturen der konventionellen Fortran-I/O herumschlagen zu müssen.
- Bei der Ein-/Ausgabe mit Streams wird die Datei als kontinuierliche Byte-Sequenz betrachtet.
- Stream-I/O ist logischerweise nicht für interne Dateien gedacht.
AnwendungBearbeiten
Auch wenn Stream-I/O in Fortran 2003 ein neues Konzept ist, so sind die altbekannten open
-, read
-, write
-und close
-Befehle dafür zuständig.
- Öffnen eines Streams:
open( ..., access = "STREAM", ... ) |
- Lesen und Schreiben:
read( ... ) ... write( ... ) ... |
- Schließen eines Streams:
close( ... ) |
Unformatierte Stream-I/OBearbeiten
Beispiel:
Fortran 2003-Code |
program bsp implicit none real :: a = 55.678 real :: b character( len = 3 ) :: str open( 50, file = "test", access = "STREAM", status = "REPLACE") write( 50 ) "Hallo Welt" write( 50 ) "Hello World" ! Ausgabe in Datei: siehe Bild 1 write( 50, pos = 100 ) "Greetings" ! Ausgabe in Datei: siehe Bild 2 write( 50, pos = 60 ) a ! Ausgabe in Datei: siehe Bild 3 read( 50, pos = 60 ) b write( *, * ) b ! Ausgabe: ! 55.678 read( 50, pos = 8 ) str write( *, * ) str ! Ausgabe: ! elt close( 50 ) end program bsp |
Da es sich um eine unformatierte Ein-/Ausgabe handelt, darf natürlich kein Formatspezifizierer bei den read
- und write
-Anweisungen angegeben werden, auch kein *
. Mittels pos
-Spezifizierer kann an eine bestimmte Position in der Datei gesprungen werden.
Bild 1 | |
---|---|
Bild 2 | |
Bild 3 |
Formatierte Stream-I/OBearbeiten
Beispiel:
Fortran 2003-Code |
program bsp implicit none real :: a = 55.678 real :: b = 13.9876 character( len = 20 ) :: str1, str2, str3 integer :: fposition open( 50, file = "test", access = "STREAM", form = "FORMATTED", status = "REPLACE") write( 50, "(2A20)" ) "Hallo Welt", "Hello World" inquire( 50, pos = fposition ) write( *, * ) fposition write( 50, * ) a, new_line( "x" ), b, " abcdef" read( 50, *, pos = fposition ) a write( *, * ) a read( 50, *, pos = 1 ) str1, str2, str3 write( *, * ) str1, str2, str3 read( 50, * ) str1, a write( *, * ) str1, a ! Ausgabe in Datei: ! Hallo Welt Hello World ! 55.678 ! 13.9876 abcdef ! Ausgabe auf Bildschirm: ! 42 ! 55.678 ! Hallo Welt Hello ! 55.678 13.9876 close( 50 ) end program bsp |
Asynchrone I/OBearbeiten
Rekursive I/OBearbeiten
SonstigesBearbeiten
WeblinksBearbeiten
Intrinsische Funktionen und SubroutinenBearbeiten
Die Fortran 90/95-Funktionen und -Subroutinen sind natürlich auch in Fortran 2003 uneingeschränkt gültig. Einige Unterprogramme wurden neu aufgenommen, andere in ihrer Funktionalität etwas erweitert.
NeuBearbeiten
DatentypfunktionenBearbeiten
Funktion | Beschreibung |
---|---|
i = selected_char_kind ( c ) |
Gibt den kind-Wert des Parameters zurück. Der Rückgabewert ist von Datentyp Parameter:
Rückgabewert:
Beispiel: i = selected_char_kind( "ASCII" ) i => 1 |
Kommandozeile und EnvironmentBearbeiten
Funktion | Beschreibung |
---|---|
i = command_argument_count ( ) | Anzahl der übergebenen Kommandozeilenargumente (der Programmname selbst wird nicht mitgezählt). Der Rückgabewert ist vom Typ integer .
Beispiel: Programmaufruf mit: ./a.out opt1 opt2 i = command_argument_count( ) i => 2 |
Subroutine | Beschreibung |
---|---|
get_command ( [c, i, i] ) | Übergebene Kommandozeilenargumente (ohne Programmname)
Parameter:
Beispiel: Programmaufruf mit: ./a.out opt1 opt2 call get_command( str, len, st ) str => opt1 opt2 len => 9 st => 0 |
get_command_argument ( i, [c, i, i] ) | Ein bestimmtes Kommandozeilenargument (inkl. Programmname)
Parameter:
Beispiel: Programmaufruf mit: ./a.out opt1 opt2 call get_command_argument( 1, str, len, st ) str => opt1 len => 4 st => 0 |
get_environment_variable ( c1, [c2, i, i, l] ) | Wert einer bestimmten Umgebungsvariable.
Parameter:
Beispiel: call get_environment_variable( "PWD", str, len, st, .TRUE. ) str => /usr/bin len => 8 st => 0 |
ErweitertBearbeiten
system_clock(i1, ir, i2)
... Das zweite Argument (count_rate
) darf nun vom Datentypinteger
oderreal
sein.max, maxloc, maxval, min, minloc, minval
... Funktionieren nunmehr auch für Werte vom Datentypcharacter
.atan2(r1, r2), log(rx), sqrt(rx)
... Unterscheidung von positiven und negativen Nullen im Argument.
Intrinsische ModuleBearbeiten
GrundlegendesBearbeiten
Module gab es bereits mit Fortran 90/95. Neu in Fortran 2003 sind die sogenannten "intrinsischen Module". Das sind jene Module, die bereits standardmäßig von Fortran-2003-Compilern bereitgestellt werden. Werden Datenelemente oder Funktionen aus solchen intrinsischen Modulen benötigt, so ist das entsprechende Modul mittels
use, intrinsic :: modulname |
in die jeweilige Programmeinheit einzubinden.
Der Unterschied zu konventionellen (nonintrinsischen) Modulen ist das Wörtchen intrinsic
, das dem Compiler mitteilt, dass er das Modul bereits mitbringt und nicht irgendwo extern danach suchen soll. Wird nach dem use
-Schlüsselwort kein entsprechendes Attribut oder das non_intrinsic
-Attribut angegeben, so zeigt dies an, dass ein nonintrinsisches Modul benutzt wird.
Das intrinsische Modul iso_fortran_env
Bearbeiten
Das iso_fortran_env
-Modul enthält einige Fortran-umgebungsspezifische Konstanten.
Beispiel:
Fortran 2003-Code |
program bsp use, intrinsic :: iso_fortran_env implicit none write( *, * ) INPUT_UNIT write( *, * ) OUTPUT_UNIT write( *, * ) ERROR_UNIT write( *, * ) IOSTAT_END write( *, * ) IOSTAT_EOR write( *, * ) NUMERIC_STORAGE_SIZE write( *, * ) CHARACTER_STORAGE_SIZE write( *, * ) FILE_STORAGE_SIZE ! Ausgabe, z.B.: ! 5 ! 6 ! 0 ! -1 ! -2 ! 32 ! 8 ! 8 end program bsp |
Erläuterung:
Konstante | Anmerkung |
---|---|
INPUT_UNIT | Standard-Eingabeeinheit (entspricht unit=* bei read )
|
OUTPUT_UNIT | Standard-Ausgabeeinheit (entspricht unit=* bei write )
|
ERROR_UNIT | Standard-Fehlerausgabeeinheit |
IOSTAT_END | end-of-file (EOF) |
IOSTAT_EOR | end-of-record (EOR) |
NUMERIC_STORAGE_SIZE | Speicherplatzbedarf (in bits) |
CHARACTER_STORAGE_SIZE | Speicherplatzbedarf (in bits) |
FILE_STORAGE_SIZE | Speicherplatzbedarf (in bits) |
All diese Konstanten sind Skalare vom Datentyp integer
.
Des Weiteren können mittels INTEGER_KINDS, REAL_KINDS, LOGICAL_KINDS
und CHARACTER_KINDS
die auf dem jeweiligen System verfügbaren kind-Parameter ermittelt werden.
Beispiel:
Fortran 2003-Code |
program bsp use, intrinsic :: iso_fortran_env implicit none print *, "integer_kinds = ", INTEGER_KINDS print *, "real_kinds = ", REAL_KINDS print *, "logical_kinds = ", LOGICAL_KINDS print *, "character_kinds = ", CHARACTER_KINDS ! Ausgabe, z.B.: ! integer_kinds = 1 2 4 8 ! real_kinds = 4 8 10 16 ! logical_kinds = 1 2 4 8 ! character_kinds = 1 4 end program bsp |
Das intrinsische Modul iso_c_binding
Bearbeiten
Das iso_c_binding
-Modul liefert die Konstanten und Unterprogramme, die für die Einbindung von C-Bibliotheken in Fortran-Programme erforderlich sind. Näheres dazu findet sich im Kapitel Fortran 2003 und C.
Die intrinsischen IEEE-ModuleBearbeiten
Die bereits aus dem dem TR 15580 : 1998 (floating-point exception handling) bekannten Module
ieee_exceptions
ieee_arithmetic
ieee_features
wurden in Fortran 2003 in Form von intrinsischen Modulen aufgenommen. Diese Module decken den IEEE 754-1985-Standard (auch IEC 559:1989) ab.
Wovon handelt der IEEE 754-1985-Standard?Bearbeiten
Im Bereich der Gleitkommazahlen (real
, float
, ...) herrschte bis in die 1980er-Jahre Anarchie. Es gab keine verbindlichen Regeln wie Gleitkommazahlen repräsentiert werden, wie gerundet wird, wie Under- und Overflows gehandhabt werden, wie mit Unendlich und NaN verfahren wird, etc. Das führte dazu, dass das gleiche Computerprogramm auf unterschiedlichen Rechnerarchitekturen und mit verschiedenen Compilern unterschiedliche Resultate liefern konnte. Um diesem Manko zu begegnen wurde in den frühen 1980er-Jahren eine Standardisierung angestrebt. Resultat war die Verabschiedung des IEEE 754-1985-Standards (IEEE Standard for Binary Floating-Point Arithmetic for microprocessor systems).
Dieser Standard regelt im Wesentlichen
- die Repräsentation von Gleitkommazahlen:
Darstellung:
s | ... | Vorzeichen |
m | ... | Mantisse |
b | ... | Basis (2 für normalisierte Zahlen) |
e | ... | Exponent |
Zahlenformate:
single | ... | 4 Bytes |
double | ... | 8 Bytes |
double-extended | ... | ≥10 Bytes, optional |
- die Darstellung normalisierter und denormalisierter Zahlen:
- Normalisierte Zahlen: Gleitkommazahlen#Normalisierung
- Denormalisierte Zahlen: Bereich zwischen der kleinsten darstellbaren normalisierten Zahl und Null (Exponent hat einen reservierten Wert, führende Bit der Mantisse ist 0)
- NaN (Not a Number), ,
- Rundungen (zur nächstgelegenen darstellbaren Zahl, in Richtung Null, in Richtung oder in Richtung )
- das Verhalten verschiedener Operationen (Grundrechenarten, Wurzelberechnung, Konvertierung Gleitkommazahl → Ganzzahl, Binär-Dezimal-Konvertierung, Vergleiche mit Nan und , etc.)
- Exception-Handling (Overflow, Underflow, Division by Zero, Inexact, Invalid)
Weiterführende Weblinks:
- IEEE 754
- Kahan, W.: Why do we need a floating-point arithmetic standard?, UC Berkeley, 1981, [4]
- Kahan, W.: Lecture Notes on the Status of IEEE Standard 754 for Binary Floating-Point Arithmetic, UC Berkeley, 1996, [5]
Implementierung in Fortran 2003Bearbeiten
Wie bereits erwähnt, besitzt Fortran 2003 intrinsische Module, mit denen der Zugriff auf bestimmte IEEE-Eigenschaften erfolgen kann. Dazu stehen eine Reihe von Funktionen, Subroutinen, Verbundtypen und Konstanten zur Verfügung. Merkmal ist, dass diese immer mit dem Präfix ieee
beginnen.
ieee_arithmetic
Bearbeiten
Das aus Programmiersicht umfangreichste Modul ist sicherlich ieee_arithmetic
. Dieses enthält zahlreiche Funktionen, Subroutinen und Konstanten.
Abfragefunktionen für die Unterstützung bestimmter IEEE-Elemente, z.B.:
l = ieee_support_datatype( [x] ) |
Prüft ob die IEEE-Arithmetik für einen speziellen real -Datentyps, charakterisiert durch x (Zahl oder Feld), unterstützt wird. Wird kein Argument angegeben, so wird geprüft, ob die IEEE-Arithmetik für alle real -Datentypen unterstützt wird.
|
l = ieee_support_nan( [x] ) |
Prüft, ob NaN-Werte unterstützt werden |
l = ieee_support_rounding( round_value, [x] ) |
Prüft, ob ein bestimmter IEEE-Rundungsmodus unterstützt wird. Mögliche Rundungsmodi sind:
|
Elementare Funktionen, z.B.:
l = ieee_is_finite( x ) |
Prüft, ob der Wert x endlich ist
|
r = ieee_next_after( x, y ) |
Liefert die nächste darstellbare Zahl von x in Richtung y
|
r = ieee_rint( x ) |
Rundet gemäß eingestelltem Rundungsmodus zu einer Ganzzahl und liefert diese Zahl mit dem Datentyp von x zurück.
|
Die Kind-Funktion:
r = ieee_selected_real_kind( [p, r] ) |
Liefert einen kind-Wert |
Nichtelementare Subroutinen, z.B.:
ieee_get_underflow_mode( gradual ) |
Liefert den aktuellen Underflow-Modus |
ieee_set_rounding_mode( round_value ) |
Setzt den IEEE-Rundungsmodus, mögliche Werte für round_value
|
ieee_exceptions
Bearbeiten
Das ieee_exceptions
-Modul enthält zwei Funktionen, mit denen abgefragt werden kann, welche Exceptions unterstützt werden bzw. inwieweit IEEE-Halting unterstützt wird:
l = ieee_support_flag( flag, [x] ) l = ieee_support_halting( flag )
Mögliche Flags sind
ieee_invalid
ieee_overflow
ieee_divide_by_zero
ieee_underflow
ieee_inexact
Des Weiteren sind in diesem Modul einige Subroutinen zum Setzen bzw. Abfragen diverser Flags enthalten:
ieee_get_status( status_value ) ieee_set_flag( flag, flag_value ) ieee_set_halting_mode( flag, halting ) ieee_set_status( status_value )
Bei Einbindung des ieee_arithmetic
-Moduls ist auch automatisch Zugriff auf die public
-Elemente des Moduls ieee_exceptions
gegeben.
ieee_features
Bearbeiten
Das ieee_features
-Modul liefert einige benannte Konstanten, z.B. ieee_datatype
, ieee_inf
, ieee_sqrt
.
Beispiel: RundungsmodusBearbeiten
IEEE-Subroutinen:
ieee_get_rounding_mode( val ) ieee_set_rounding_mode( flag )
Mögliche Wert für flag
sind:
|
... | default, Rundung zur nächstgelegenen Zahl (wenn das nicht eindeutig möglich ist, dann Rundung zur nächstgelegenen geraden Zahl) |
|
... | Rundung in Richtung 0 |
|
... | Rundung Richtung |
|
... | Rundung Richtung |
Fortran 2003-Code |
program bsp use, intrinsic :: ieee_arithmetic implicit none real, dimension(6) :: a = (/ -1.5, -0.5, 0.5, 1.5, 2.5, 3.5 /) ! Standard-Fortran-Rundungsfunktion write( *, * ) anint( a ) ! IEEE-Rundungsfunktion (default) write( *, * ) ieee_rint( a ) ! IEEE-Rundungsfunktion mit Flag ieee_round_type = ieee_nearest call ieee_set_rounding_mode( ieee_nearest ) write( *, * ) ieee_rint( a ) ! IEEE-Rundungsfunktion mit Flag ieee_round_type = ieee_to_zero call ieee_set_rounding_mode( ieee_to_zero ) write( *, * ) ieee_rint( a ) ! IEEE-Rundungsfunktion mit Flag ieee_round_type = ieee_down call ieee_set_rounding_mode( ieee_down ) write( *, * ) ieee_rint( a ) ! IEEE-Rundungsfunktion mit Flag ieee_round_type = ieee_up call ieee_set_rounding_mode( ieee_up ) write( *, * ) ieee_rint( a ) end program bsp |
Ausgabe:
Standard-Fortran | IEEE-Default | ieee_nearest | ieee_to_zero | ieee_down | ieee_up |
---|---|---|---|---|---|
-2.0 -1.0 1.0 2.0 3.0 4.0 |
-2.0 0.0 0.0 2.0 2.0 4.0 |
-2.0 0.0 0.0 2.0 2.0 4.0 |
-1.0 0.0 0.0 1.0 2.0 3.0 |
-2.0 -1.0 0.0 1.0 2.0 3.0 |
-1.0 0.0 1.0 2.0 3.0 4.0 |
Beispiel: Halting-ModusBearbeiten
Dieser Modus bestimmt, ob nach einer Exception das Programm angehalten oder fortgesetzt wird. Die in diesem Beispiel eingesetzten IEEE-Unterprogramme sind:
ieee_support_halting( flag )
... prüft, ob auf dem System das IEEE-Halting für das angegebenen Flag überhaupt unterstützt wird (Rückgabewert:.true.
). Mögliche Flags:ieee_invalid
ieee_overflow
ieee_divide_by_zero
ieee_underflow
ieee_inexact
ieee_set_halting_mode( flag, mode )
... setzt den Halting-Modus für ein bestimmtes Flag.flag
... wie beiieee_support_halting( flag )
mode
:.true.
... anhalten.false.
... Programm weiter ausführen
Fortran 2003-Code |
program main use, intrinsic :: ieee_arithmetic implicit none real :: a, b if( ieee_support_halting( ieee_divide_by_zero ) ) then call ieee_set_halting_mode( ieee_divide_by_zero, .false. ) read( *, * ) a, b write( *, * ) "Resultat: ", a / b write( *, * ) "Programm wird fortgesetzt ..." else write( *, * ) "IEEE-Halting wird nicht unterstuetzt" end if end program main |
Eingabe:
- 10.0 (... für a)
- 0.0 (... für b)
Ausgabe (bei Programmerstellung mit dem Sun-Express-Fortran-Compiler f95):
ieee_set_halting_mode( ieee_divide_by_zero, .false. )
|
ieee_set_halting_mode( ieee_divide_by_zero, .true. )
|
---|---|
Resultat: Inf Programm wird fortgesetzt ... |
Gleitkomma-Ausnahme |
Weitere Beispiele zum Thema "Exceptions and IEEE arithmetic" sind im Fortran 2003-Working Draft J3/04-007 ab Seite 386 enthalten.
Eine Programmbibliothek bezeichnet in der Programmierung eine Sammlung von Programmfunktionen für zusammengehörende Aufgaben. Bibliotheken sind im Unterschied zu Programmen keine eigenständig lauffähigen Einheiten, sondern Hilfsmodule, die Programmen zur Verfügung gestellt werden.
QuelltextbibliothekenBearbeiten
Quelltextbibliotheken enthalten Sammlungen von Wertedefinitionen, Deklarationen, Funktionen, Klassen, generischen Bestandteilen, usw.
APIBearbeiten
Eine Programmierschnittstelle ist eine Schnittstelle die von einem Softwaresystem anderen Programmen zur Anbindung an das System zur Verfügung gestellt wird. Oft wird dafür die Abkürzung API (für engl. application programming interface, deutsch: Schnittstelle zur Anwendungsprogrammierung) verwendet. Im Gegensatz zu einer Binärschnittstelle (ABI) definiert ein API nur die Verwendung der Schnittstellen auf Quelltextebene.
Neben dem Zugriff auf Datenbanken, die Hardware wie Festplatte oder Grafikkarte kann ein API auch das Erstellen von Komponenten der grafischen Benutzeroberfläche ermöglichen oder vereinfachen.
Im weiteren Sinne wird die Schnittstelle jeder Bibliothek (Library) als API bezeichnet.
Statische BibliothekenBearbeiten
Statische Bibliotheken werden nach dem Kompiliervorgang durch einen so genannten Linker oder Binder in einem eigenen Schritt mit dem ausführbaren Programm verbunden.
Der Linker sucht aus den Bibliotheksdateien Unterprogramme heraus, für die es im Programm keine Implementierung gibt. Diese werden dann aus den Dateien extrahiert und an das Programm gebunden, d.h. der Unterprogrammcode wird an den Programmcode angefügt und die Aufrufverweise werden auf die Unterprogrammadressen gerichtet.
Dynamische BibliothekenBearbeiten
Dynamische Bibliotheken werden erst bei Bedarf in den Arbeitsspeicher geladen und durch den sogenannten Lader mit dem ausführbaren Programm verbunden. Dadurch muss eine Bibliothek, die von mehreren Programmen genutzt wird, nur einmal im Speicher gehalten werden.
Dies ist beispielsweise bei Multitasking-Systemen vorteilhaft, wenn die Bibliotheken insgesamt sehr groß sind und von vielen Prozessen gleichzeitig verwendet werden. Dort wird eine Bibliotheksdatei bei ihrer ersten Verwendung in den Speicher geladen. Trifft ein Programm auf den Verweis zu einem Unterprogramm, das noch nicht eingebunden wurde, dann wird ein Laufzeitbinder aktiviert. Dieser sucht das Unterprogramm in den im Speicher vorhandenen Bibliotheken, fügt die Adresse am Aufrufpunkt ein und führt das Unterprogramm erstmalig aus.
Bei jedem weiteren Aufruf des Unterprogramms ist dann die Adresse vorhanden, so dass das Unterprogramm direkt aufgerufen wird. Die Ausführungszeit, insbesondere die Startzeit eines Programms, ist hier geringfügig erhöht. Dies wird in Kauf genommen, da der Programmcode der Bibliotheksfunktionen von allen Prozessen geteilt wird. Der Speicherbedarf aller Programme zusammen ist daher in der Regel kleiner als beim statischen Linken.
Unterstützt das Betriebssystem virtuellen Speicher, so entfällt das Laden der gesamten Bibliothek bei der ersten Verwendung. Stattdessen wird die Bibliothek in den Speicherbereich jedes sie verwendenden Prozesses eingeblendet. Die virtuelle Speicherverwaltung lädt danach nur tatsächlich benötigte Teile der Bibliothek bei Bedarf von der Festplatte in den Arbeitsspeicher.
Bibliotheken in verschiedenen ProgrammiersprachenBearbeiten
Bibliotheken in Programmiersprachen enthalten Leistungen, die nicht im Compiler implementiert sind, sondern in der Sprache selbst programmiert sind und mit dem Compiler zusammen oder völlig von ihm getrennt dem Programmierer zur Verfügung stehen. Im ersten Fall ist die Bibliothek meist in der Sprachbeschreibung festgelegt. Im zweiten Fall spricht man von einer externen Bibliothek.
Bibliotheken bei verschiedenen BetriebssystemenBearbeiten
WindowsBearbeiten
Bei den Betriebssystemen Windows und auch bei OS/2 wird eine Bibliotheksdatei, die dynamisch bindet, als Dynamic Link Library (DLL) bezeichnet. Entsprechend haben diese Dateien meist die Dateiendung .dll. Ihr Dateiformat ist Portable Executable.
Problematisch ist bei Windows 95, Windows 98 und Windows Me, dass durch unzureichende Schutzmaßnahmen die DLLs nicht kontrolliert werden - jedes Programm darf sie austauschen und kann dem Betriebssystem damit möglicherweise Schaden zufügen. Windows 2000 und Windows XP hingegen verfügen über einen Systemschutz, der auch die DLLs einbezieht.
VorteileBearbeiten
- Außer Code können auch Daten (z. B. Dialog-Ressourcen) von mehreren Prozessen gemeinsam genutzt werden.
- DLLs werden häufig statisch gelinkt, können aber auch dynamisch (daher der Name) gelinkt werden. Dynamisch heißt hier, dass die DLL explizit vom Programm zur Laufzeit geladen wird und die Funktionen, die sich in der DLL befinden, „per Hand“ mit dem Programm verbunden werden. Dadurch wird es möglich, durch Austauschen der DLL die Funktionalität des Programms zur Laufzeit zu verändern.
- DLLs können unabhängig vom Hauptprogramm gewartet werden. D. h. Funktionen in der DLL können ohne Wissen des Programms verändert werden. Danach wird die DLL einfach ausgetauscht (die alte DLL-Datei wird überschrieben), ohne dass das Hauptprogramm verändert werden muss.
- Da die DLL als unabhängige Datei dem Hauptprogramm beiliegen muss, können Anbieter von Programmcode besser sicherstellen, dass Programmierer, die die Funktionen ihrer DLL nutzen, dafür auch bezahlen. Die Funktionalität der DLL verschwindet so nicht (wie bei einer Library) im Code des Programms. Dieser Vorteil wird von Befürwortern freier Software als Nachteil gesehen.
NachteileBearbeiten
Änderungen in DLLs ziehen oft auch Änderungen im Programm mit sich. Dadurch kommt es leicht zu Versionskonflikten, die oft nur sehr schwer aufzuspüren sind.
Eine der Grundideen der DLLs war, Programmcode zwischen mehreren Programmen zu teilen, um so kostbaren Speicher zu sparen. In der Praxis ist es jedoch dazu gekommen, dass viele Programme bei der Installation DLLs in das Windows-Systemverzeichnis schreiben, die außer diesem speziellen Programm kein anderes benutzen kann.
Außerdem ist die Entwicklung und insbesondere die Anbindung im Vergleich aufwändiger als zur statischen Bibliothek.
QuintessenzBearbeiten
DLLs sollte man nur benutzen, wenn man ihre spezielle Funktionalität benötigt und man ausschließlich unter Windows arbeitet. Sind statische Bibliotheken für den Zweck ausreichend, sollte man diese vorziehen. In der Praxis ergeben sich keinerlei Einsparungen bei der Größe des Codes.
Unix-artigeBearbeiten
Auf Unix-artigen Betriebssystemen ist für dynamische Bibliotheken die Bezeichnung shared library (englisch shared, geteilt) gebräuchlich.
Für diese Dateien hat sich die Endung .so (shared object) eingebürgert. In der Regel folgt dem Bibliotheksnamen noch eine Versionsnummer.
Grafik und GUIBearbeiten
DISLINBearbeiten
AllgemeinesBearbeiten
Die DISLIN Scientific Plotting Software ist eine Bibliothek für die grafische Datendarstellung. Auch für die Gestaltung grafischer Benutzeroberflächen läßt sich DISLIN verwenden. DISLIN greift zu diesem Zwecke auf die Motif-Bibliothek zu. Die DISLIN-Bibliothek ist für mehrere Programmiersprachen konzipiert, so auch auch für die Programmiersprache Fortran.
BeispieleBearbeiten
Beispiel 1: Strings und ZahlenBearbeiten
Fortran 90/95-Code (free source form) |
program dbsp1 implicit none real, parameter :: PI = 3.1415926 ! *** Initialisierung *** call setpag ("DA4P") ! DIN-A4 Hochformat call metafl ("CONS") ! Ausgabe auf Konsole (Bildschirm) call disini ! DISLIN initialisieren ! *** Zeichnen *** call messag ("Hallo, Welt!", 50,50) ! Message schreiben call number (PI, 4, 50, 150) ! 3.1416 schreiben ! *** Aufräumen *** call disfin ! DISLIN beenden end program dbsp1 |
Programm erstellen:
- Variante 1:
gfortran -c dateiname.f95 dlink dateiname
- Variante 2:
gfortran -o dateiname dateiname.f95 -ldislin
Eine Auswahl von möglichen aktuellen Parametern für die Subroutine metafl
:
- "CONS" ... Konsole (Bildschirm)
- "XWIN" ... X-Window (Bildschirm)
- "EPS" ... Encapsulated Postscript-Datei
- "PNG" ... PNG-Datei
- "SVG" ... SVG-Datei
- "PDF" ... PDF-Datei
Beispiel 2: Zeichnen von Kurven und FunktionenBearbeiten
Fortran 90/95-Code (free source form) |
program dbsp2 implicit none real, dimension(0:99) :: x, y integer :: i, setrgb do i = 0, 99 x(i) = i / 20.0 y(i) = sin(x(i)) end do ! *** Initialisierung *** call setpag ("DA4P") ! DIN-A4 call metafl ("PNG") ! Ausgabe in eine PNG-Datei call disini ! DISLIN initialisieren ! *** Zeichnen *** call pagfll (255) ! Hintergrundfarbe auf weiß setzen call color (setrgb (0., 0., 0.)) ! Vordergrundfarbe auf schwarz setzen call graf (0.0, 5.0, 0.0, 0.5, -1.0, 1.0, -1.0, 0.1) ! 2D-Koordinatensystem setzen call curve (x, y, 100) ! Graphen zeichnen ! *** Aufräumen *** call disfin ! DISLIN beenden end program dbsp2 |
Beispiel 3: Ein Pie-ChartBearbeiten
Fortran 90/95-Code (free source form) |
program dbsp3 implicit none real, dimension(3) :: part = (/5.5, 2.5, 1.0/) integer, dimension(3) :: partcol1 = (/10, 100, 150/) integer, dimension(3) :: partcol2 = (/10, 100, 150/) integer :: setrgb ! *** Initialisierung *** call setpag ("DA4P") ! DIN-A4 call metafl ("CONS") ! Console (Bildschirm) call disini ! DISLIN initialisieren ! *** Zeichnen *** call pagfll (255) ! Hintergrundfarbe auf weiß setzen call color (setrgb (0., 0., 0.)) ! Vordergrundfarbe auf schwarz setzen call shdpat (16) ! Shadingpattern (16 = voll) call chnpie ("NONE") ! Farbe und Shadingpattern call pieclr (partcol1, partcol2, 3) ! Teilfarben call pietyp ("3D") ! 3D call piegrf ("Hallo", 0 , part, 3) ! Pie-Chart zeichnen ! *** Aufräumen *** call disfin ! DISLIN beenden end program dbsp3 |
Beispiel 4: Ein MeldungsfensterBearbeiten
Fortran 90/95-Code (free source form) |
program dbsp4 implicit none call disini call dwgmsg ("Hallo, Welt") call disfin end program dbsp4 |
Weitere (auch komplexere) Beispiele finden sich im ausführlichen DISLIN-Manual. Dieses ist auf der unten genannten Webpräsenz abrufbar.
WeblinksBearbeiten
f03glBearbeiten
AllgemeinesBearbeiten
f03gl ist ein Fortran 2003-Interface für OpenGL, speziell für GLUT, freeglut und OpenGLUT. f03gl kann als der Nachfolger von f90gl betrachtet werden.
BeispielBearbeiten
Fortran 2003-Code |
module bsp_ogl contains subroutine display() use opengl_gl use opengl_glut implicit none call glclear(GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT) call glColor3f( 0.2, 1.0, 0.3 ) call glutSolidTeapot( 50.0_gldouble ) call glutswapbuffers end subroutine display subroutine gfxinit use opengl_gl use opengl_glu implicit none real( glfloat ), dimension( 4 ) :: pos = (/ 100.0, 100.0, 200.0, 1.0 /) call glenable( GL_LIGHTING ) call glenable( GL_LIGHT0 ) call glenable( GL_DEPTH_TEST ) call glenable( GL_COLOR_MATERIAL ) call glenable( GL_NORMALIZE ) call glenable( GL_POLYGON_SMOOTH ) call gllightfv( GL_LIGHT0, GL_POSITION, pos ) call glClearColor( 0.7, 0.7, 0.7, 0.0 ) call glmatrixmode( GL_PROJECTION ) call glOrtho( -100.0_gldouble, 100.0_gldouble, -100.0_gldouble, 100.0_gldouble, & -100.0_gldouble, 100.0_gldouble ); call glmatrixmode( GL_MODELVIEW ) call glrotatef( 35.0, 1.0, 0.0, 0.0 ) call glrotatef( -25.0, 0.0, 1.0, 0.0 ) end subroutine gfxinit end module bsp_ogl program bsp use opengl_glut use bsp_ogl use, intrinsic :: iso_c_binding implicit none integer :: i call glutinit() call glutinitdisplaymode( GLUT_DOUBLE + GLUT_RGB + GLUT_DEPTH ) i = glutcreatewindow( "Beispiel" // c_null_char ) call gfxinit call glutdisplayfunc( display ) call glutmainloop end program bsp |
Um das Beispiel nutzen zu können, müssen die Dateien OpenGL_gl.f90, OpenGL_glu.f90 und wahlweise OpenGL_glut,f90, OpenGL_freeglut.f90 oder OpenGL_openglut.f90 von der Webseite „Fortran 2003 Interface to OpenGL" herunter geladen werden. Die ersten beiden Dateien sind das Interface zu den beiden Bibliotheken GL (Graphics Library) und GLU. Die drei anderen Dateien sind das Interface für die Utility Toolkits GLUT, freeglut bzw. OpenGLUT.
Diese Dateien müssen dann kompiliert werden, beispielsweise für die Verwendung der freeglut:
g95 -c OpenGL_gl.f90 g95 -c OpenGL_glu.f90 g95 -c OpenGL_freeglut.f90
Kompilieren und Linken des Beipielprogrammses bsp.f03, hier als Beispiel, wenn sich die opengl_*.mod-Dateien und OpenGL_*.o-Dateien im gleichen Verzeichnis wie die Beispieldatei bsp.f03 befinden und die freeglut-Bibliothek verwendet wird (wie in einigen Linux-Distrubtionen wie z. B. Red Hat):
g95 -o bsp bsp.f03 OpenGL_gl.o OpenGL_glu.o OpenGL_freeglut.o -lGL -lGLU -lglut
Die Parameter -lGL, -lGLU geben die Pfade der beiden Bibliotheken GL und GLU an, während -lglut den Pfad des Utility Toolkits angibt.
Für Mac OS X ist folgende Sequenz zu verwenden:
g95 -o bsp bsp.f03 OpenGL_gl.o OpenGL_glu.o OpenGL_glut.o -framework Carbon -framework OpenGL -framework GLUT
Nach der Eingabe von:
./bsp
sollte als Ausgabe die Utah-Teekanne erscheinen:
BesonderheitenBearbeiten
Die Routinen für OpenGl und die GLUT sind in C geschrieben und auch die meisten Anwendungen von OpenGL und GLUT erfolgen in C. Daraus ergeben sich einige Besonderheiten für die Einbindung von OpenGL und GLUT unter Fortran.
In C lässt sich folgender Code erzeugen:
int main( int argc, char* argv[]) { ... glutReshapeFunc(ChangeSize); ... return 0; }
void ChangeSize(GLsizei w, GLsizei h) { ... }
Dabei ist GLsizei ein von OpenGL spezifizierter Datentyp vom Typ integer.
In Fortran 2003 sieht das selbe Code-Fragement unter Verwendung der C-Interoperabilität von Fortran 2003 zum Beispiel folgenderweise aus:
Fortran 2003-Code |
module simple_opengl_things use opengl_gl use opengl_glu use opengl_glut contains ... subroutine ChangeSize(w, h) bind(c) implicit none integer( kind=GLsizei ), value :: w integer( kind=GLsizei ), value :: h ... end subroutine ChangeSize ... end module simple_open_gl_things program simple_opengl use opengl_glut use simple_opengl_things ... call glutReshapeFunc( ChangeSize ) ... end program simple_opengl |
WeblinksBearbeiten
JapiBearbeiten
AllgemeinesBearbeiten
Auch für den Bereich des Graphical-User-Interface-Building sind Fortran-Bibliotheken verfügbar. Einerseits gibt es kommerziell-proprietäre Bibliotheken wie Interacter, Winteracter oder GinoMenu, die allerdings eindeutig auf rein professionellen Einsatz abzielen und deren Preise auch dementsprechend hoch liegen.
Hier soll darum anhand eines einfachen Beispiels auf eine mögliche Open-Source-Alternative zu diesen kommerziellen Bibliotheken hingewiesen werden, nämlich japi. japi steht unter der GNU Lesser General Public License und ist ein Java AWT-Wrapper. japi ist für verschiedene Programmiersprachen erhältlich, so auch für FORTRAN 77. Mit kleineren Adaptierungen kann diese Bibliothek aber auch mit Fortran 90/95 verwendet werden. Mit japi ist derzeit nur ein Teil der AWT-Möglichkeiten abrufbar. Der Einsatz von japi setzt zwingend eine aktuelle Java-Installation (JRE oder JDK) auf dem Computer voraus.
japi-InstallationBearbeiten
- Download der Dateien "japi.f" und "libjapi.zip" von der japi-Homepage.
- Entpacken der "libjapi.zip" (die Linux-Version enthält z.B. nur die "libjapi.a"-Bibliotheksdatei).
- Verschieben der Bibliotheksdatei in ein geeignetes Verzeichnis, z.B. unter Linux in "/usr/lib" oder "/usr/local/lib".
BeispielBearbeiten
Fortran 90/95-Code (free source form) |
program jbsp include "japi.f95" integer :: frame, obj integer, dimension(5) :: button if( .not. j_start()) then write(*,*) "JAPI-Problem" call end () end if frame = j_frame("JAPI-Beispiel") call j_setborderlayout(frame) button(1) = j_button(frame, "Button 1") button(2) = j_button(frame, "Button 2") button(3) = j_button(frame, "Button 3") button(4) = j_button(frame, "Button 4") button(5) = j_button(frame, "Button 5") call j_setborderpos(button(1), J_LEFT) call j_setborderpos(button(2), J_RIGHT) call j_setborderpos(button(3), J_TOP) call j_setborderpos(button(4), J_BOTTOM) call j_show(frame) do obj=j_nextaction() if(obj == frame) call end () if(obj == button(1)) write (*,*) "Button 1 gedrückt" if(obj == button(2)) write (*,*) "Button 2 gedrückt" if(obj == button(3)) write (*,*) "Button 3 gedrückt" if(obj == button(4)) write (*,*) "Button 4 gedrückt" if(obj == button(5)) write (*,*) "Button 5 gedrückt" end do call end () end program jbsp subroutine end call j_quit() stop end subroutine end |
Die Include-Datei "japi.f95" ist eine adaptierte "japi.f"-Datei. "japi.f" ist als Teil der japi-Bibliothek im FORTRAN 77-Format auf der unten genannten japi-Webseite zu finden. Für eine Minimalanpassung müssen nur die C-Kommentarzeichen aus FORTRAN 77 gegen die !-Kommentarzeichen von Fortran 95 ausgewechselt werden.
Kompilieren, Linken:
gfortran -o jbsp jbsp.f95 -ljapi
Reference Manuals, sowie Programming Manuals zu japi sind auf der nachfolgend angeführten Homepage in verschiedenen Dateiformaten abrufbar.
WeblinksBearbeiten
PilibBearbeiten
AllgemeinesBearbeiten
Auch pilib ist ein Open-Source-Ansatz für die Erstellung von GUIs mittels Fortran. Anders als japi verwendet pilib zu diesem Zweck die GTK+-Bibliothek. Momentan befindet sich dieses Projekt in einer frühen Entwicklungsphase (Alpha-Status, Stand: Anfang 2006).
Für nähere Informationen hinsichtlich der GTK+-Bibliothek wird auf die GTK+-Homepage verwiesen.
pilib-InstallationBearbeiten
- Download des pilib-Softwarepakets von der im Abschnitt Weblinks angegebenen pilib-Internetadresse.
- Entpacken (gunzip, tar).
- Installation der Bibliotheksbestandteile für Linux mit dem üblichen ./configure, make, make install.
Für eine detailliertere Installationsanleitung wird auf die im Softwarepaket enthaltene INSTALL- und README-Datei, sowie das pilib-Manual im HTML-Format verwiesen.
BeispielBearbeiten
Fortran 90/95-Code (free source form) |
module bspmod implicit none save integer :: myedit1, myedit2, myedit3 end module bspmod program bsp use pimod use bspmod implicit none integer :: mywin, mycontainer, mybutton, mytext, myclose, myclick call piinit call gkwindow(c("Addition"), 1, 0, mywin, myclose) ! Container (in diesem Fall eine Table) call gkcontain(3, 2, 4, 5, mycontainer) call gkput(0, 0, -1, -1, mywin, mycontainer) ! Label call gktext(c("Zahl 1: "), mytext) call gkputtable(0, 0, 0, 0, 4, 4, 5, 5, -1, -1, mycontainer, mytext) ! Einzeiliges Eingabefeld mit einer Breite von 10 Zeichen call gkxedt(10, myedit1) call gkputtable(1, 0, 1, 0, 4, 4, 5, 5, -1, -1, mycontainer, myedit1) ! Label call gktext(c("+"), mytext) call gkputtable(0, 1, 1, 1, 4, 4, 5, 5, -1, -1, mycontainer, mytext) ! Label call gktext(c("Zahl 2: "), mytext) call gkputtable(0, 2, 0, 2, 4, 4, 5, 5, -1, -1, mycontainer, mytext) ! Einzeiliges Eingabefeld mit einer Breite von 10 Zeichen call gkxedt(10, myedit2) call gkputtable(1, 2, 1, 2, 4, 4, 5, 5, -1, -1, mycontainer, myedit2) ! Schaltfläche call gkbutton(c("="), mybutton, myclick) call gkputtable(0, 3, 2, 3, 4, 4, 5, 5, -1, -1, mycontainer, mybutton) ! Label call gktext(c("Ergebnis: "), mytext) call gkputtable(0, 4, 0, 4, 4, 4, 5, 5, -1, -1, mycontainer, mytext) ! Einzeiliges Eingabefeld mit einer Breite von 10 Zeichen call gkxedt(10, myedit3) call gkputtable(1, 4, 1, 4, 4, 4, 5, 5, -1, -1, mycontainer, myedit3) call gkshow(mywin) do while(myclose == 0) call gkproc if(myclick /= 0) then call calculate myclick = 0 end if end do call gkdestroy(mywin) end program bsp subroutine calculate use pimod use bspmod implicit none real :: k1, k2, string2real character(30) :: cstr k1 = string2real(myedit1) k2 = string2real(myedit2) write(cstr, *) k1+k2 call gksetstring (c(cstr), myedit3) end subroutine calculate function string2real(widget) use pimod implicit none real :: string2real, zahl integer, intent(in) :: widget character(30) :: cstr type(string) :: str call gkgetstring(str, widget) cstr = f_str2char(str) read(cstr, *) zahl ! Umwandlung eines character-Wertes in eine real-Zahl ! unter Zuhilfenahme des internal-file-Mechanismus string2real = zahl end function string2real |
Kompilieren, Linken:
g95 bsp.f95 -lpilib -lpilibf -I/usr/local/include
Bei der pilib-Installation werden mod-Dateien in ein Standard-Include-Verzeichnis geschrieben. Der Optionsschalter "-I" weist den Compiler an, im gegebenem Verzeichnis nach Include-Dateien zu suchen, in diesem Fall nach mod-Dateien. Das Format der mod-Dateien ist compilerabhängig.
Dieses Beispiel soll nur einen ersten Eindruck von pilib geben. Eine genauere Beschreibung der verwendeten pilib-Unterprogramme und Subroutinenparameter, sowie eine Auflistung weiterer Möglichkeiten der pilib-Bibliothek wird hier mit Hinweis auf die dem pilib-Softwarepaket beiliegenden Dokumentationsdateien nicht getätigt.
WeblinksBearbeiten
MathematikBearbeiten
BLAS und ATLASBearbeiten
AllgemeinesBearbeiten
Die Basic Linear Algebra Subprograms (BLAS) stellen eine Sammlung von Unterprogrammen für die Vektor- und Matrizenrechnung dar.
- Level 1: Skalar-Vektor-, Vektor-Vektor-Operationen
- Level 2: Matrix-Vektor-Operationen
- Level 3: Matrix-Matrix-Operationen
Die Automatically Tuned Linear Algebra Software (ATLAS) ist ein um einige LAPACK-Funktionen erweitetertes BLAS-Paket und bietet die Möglichkeit, automatisiert eine rechneroptimierte Algebra-Bibliothek zu erzeugen.
Installation von BLASBearbeiten
BLAS wird in Form von Fortran-Quellcodedateien in einem gepackten tar-Paket zur Verfügung gestellt. Ein Makefile zur Generierung einer Bibliotheksdatei wird nicht mitgeliefert. Eine derartige Bibliotheksdatei kann aber einfach selbst erstellt werden. Eine Anleitung findet sich z.B. auf der gfortran-Dokumentationsseite. Die notwendigen Schritte sind:
- blas.tgz downloaden
- Dieses Paket in ein leeres Verzeichnis entpacken
- Bibliothek erstellen ("shared library" oder "static library"):
- In Form einer "shared library":
gfortran -shared -O2 *.f -o libblas.so -fPIC - In Form einer "static library":
gfortran -O2 -c *.f
ar cr libblas.a *.o
- In Form einer "shared library":
- Die daraus resultierende Bibliotheksdatei in ein geeignetes Verzeichnis verschieben (z.B. /usr/lib/ oder /usr/local/lib/)
BeispieleBearbeiten
Beispiel: Die Level 1-Funktionen sdot und dnrm2Bearbeiten
Fortran 90/95-Code (free source form) |
program bsp implicit none real, dimension(3) :: a = (/2.,1.,-1./), b = (/5., -2., 1.5/) real :: c, sdot real(kind=8) :: d, dnrm2 ! *** Skalarprodukt *** ! sdot: s ... REAL, dot ... Skalarprodukt (inneres Produkt) ! 1. Argument ... Dimension des Vektors ! 2. und 4. A. ... die Vektoren ! 3. und 5. A. ... Inkrement (hier 1) c = sdot(3, a, 1, b, 1) write(*,*) c ! Ausgabe: 6.500000 ! *** Norm des Vektors *** ! dnrm2: d ... DOUBLE PRECISION, nrm2 ... (euklidische) Norm ! 1. Argument: Dimension des Vektors ! 2. A.: Vektor ! 3. A.: Inkrement (hier 1) d = dnrm2(3, dble(a), 1) write(*,*) d ! Ausgabe: 2.44948974278318 end program bsp |
Kompilieren und Linken:
gfortran bsp.f95 -lblas
Beispiel: Die Level 2-Subroutine sgerBearbeiten
sger steht für:
- s ... REAL
- ge ... general matrix
- r ... rank 1 operation
Mathematisch ist damit folgende Operation gemeint:
wobei A eine mxn-Matrix ist, x und y stellen Vektoren dar.
Das nachfolgende Beispiel führt konkret folgende Rechnung aus:
Fortran 90/95-Code (free source form) |
program bsp implicit none real, dimension(2) :: x = (/2., 1./), y = (/1., 0./) real, dimension(2,2) :: a = reshape ( (/1., 2., -1., 7./), (/2, 2/) ) call sger (2, 2, 1., x, 1, y, 1, a, 2) write(*,*) a ! Ausgabe: 3.000000 3.000000 -1.000000 7.000000 end program bsp |
WeblinksBearbeiten
FGSLBearbeiten
AllgemeinesBearbeiten
Bei der "GNU Scientific Library" (GSL) handelt es sich um eine in C geschriebene Bibliothek. Diese bietet Funktionen für ein weites Spektrum der Mathematik. Beispielhaft seien folgende Bereiche genannt:
- Komplexe Zahlen
- Lineare Algebra
- Polynome
- Statistik
- Fast Fourier Transformation (FFT)
- Numerische Differentiation
- Numerische Integration
- Gewöhnliche Differentialgleichungen
- IEEE Floating-Point-Arithmetik
FGSL ist ein Fortran-Interface für diese GSL-Bibliothek. FGSL selbst deckt nicht die komplette Funktionspalette von GSL ab, inzwischen sind aber auch lineare Algebra und die FFT-Funktionen Bestandteil von FGSL, auch wenn zu diesem Zweck der Einsatz der optimierten Bibliotheken LAPACK und FFTW empfohlen wird. Für einige Teilbereiche werden nicht alle in C implementierten Datentypen unterstützt.
FGSL wurde unter Verwendung einiger Fortran 2003-Sprachmerkmale erstellt. Die Einbindung von FGSL in eigene Programme setzt aus diesem Grunde einen entsprechenden Fortran-Compiler voraus. Der g95-Compiler erfüllt z.B. diese Voraussetzungen.
Derzeit ist die FGSL-Version 0.9.4 vom 31. Mai 2011 aktuell.
BeispieleBearbeiten
Beispiel: Datentypen, Potenzierung und mathematische KonstantenBearbeiten
C | Fortran |
---|---|
#include <stdio.h> #include <gsl/gsl_math.h> int main (void) { double a; double d = 5.0; a = gsl_pow_2 (d) * M_PI_4; printf ("Kreisflaeche = %f\n", a); return 0; } |
program bsp use fgsl implicit none real( kind = fgsl_double ) :: a real( kind = fgsl_double ) :: d = 5.0_fgsl_double a = d ** 2 * m_pi_4 write( *, * ) "Kreisflaeche = ", a end program bsp |
Kompilieren, Linken:
|
Kompilieren, Linken:
|
Der kind
-Wert fgsl_double
entspricht dem c_double
aus dem iso_c_binding
-Modul. FGSL kennt die speziellen pow
-Funktionen aus GSL nicht, da Fortran ohnehin über einen eigenen Potenzierungsoperator verfügt. Neben der m_pi_4
-Konstante ( = ) kennt FGSL noch eine ganze Reihe anderer mathematischer Konstanten, z.B.:
m_e |
... | e, Eulersche Zahl, 2,714... |
m_euler |
... | Eulersche Konstante, 0,577... |
m_pi |
... | |
m_pi_2 |
... | |
m_sqrt2 |
... |
Auch jede Menge physikalische Konstanten kennt FGSL, z.B.:
fgsl_const_mksa_speed_of_light |
... | Lichtgeschwindigkeit im Vakuum, 2.9979... x 108 m / s |
fgsl_const_mksa_molar_gas |
... | Allgemeine Gaskonstante, 8.314472 J / (K · mol) |
fgsl_const_mksa_inch |
... | 0.0254 m |
fgsl_const_mksa_torr |
... | 133.322... Pa |
fgsl_const_num_giga |
... | 109 |
Beispiel: Lösen einer quadratischen GleichungBearbeiten
Gesucht ist die Lösung der quadratischen Gleichung
C | Fortran |
---|---|
#include <stdio.h> #include <gsl/gsl_complex.h> #include <gsl/gsl_poly.h> int main (void) { double a, b, c; gsl_complex z1, z2; int i; a = 1.0; b = 12.0; c = 37.0; i = gsl_poly_complex_solve_quadratic(a, b, c, &z1, &z2); if(i == 1) { /* nur eine Lsg.*/ printf("z = (%f, %f)\n", z1.dat[0], z1.dat[1]); } else { /* 2 Lsg., reell oder komplex */ printf( "z1 = (%f, %f)\n", z1.dat[0], z1.dat[1]); printf( "z2 = (%f, %f)\n", z2.dat[0], z2.dat[1]); } return 0; /* Ausgabe: z1 = (-6.000000, -1.000000) z2 = (-6.000000, 1.000000) */ } |
program bsp use fgsl implicit none real( kind = fgsl_double ) :: a, b, c complex( fgsl_double ), dimension( 2 ) :: z integer( kind = fgsl_int ) :: i a = 1.0_fgsl_double b = 12.0_fgsl_double c = 37.0_fgsl_double i = fgsl_poly_complex_solve_quadratic( a, b, c, z(1), z(2) ) if( i == 1 ) then ! nur eine Lsg. write( *, * ) " z =", z(1) else ! 2 Lsg., reell oder komplex write( *, * ) " z1,2 =", z end if ! Ausgabe: ! z1,2 = (-6.,-1.) (-6.,1.) end program bsp |
Beispiel: Numerische IntegrationBearbeiten
Gesucht ist die Lösung des Integrals
C | Fortran |
---|---|
#include <stdio.h> #include <math.h> #include <gsl/gsl_integration.h> double f(double x, void *params) { return(sin(x) / x); } int main () { double result, error; size_t neval; gsl_function func; func.function = &f; func.params = 0; gsl_integration_qng (&func, 0.0, 1.0, 1e-9, 1e-9, &result, &error, &neval); printf ("Ergebnis = %f\n", result); return 0; /* Ausgabe: Ergebnis = 0.946083 */ } |
module integral implicit none contains function f( x, params ) bind(c) use, intrinsic :: iso_c_binding implicit none real( kind = c_double ) :: f real( kind = c_double ), value :: x type( c_ptr ), value :: params f = sin( x ) / x end function f end module integral program bsp use fgsl use integral use, intrinsic :: iso_c_binding implicit none real( kind = fgsl_double ) :: result, error integer( kind = fgsl_size_t) :: neval integer( kind = fgsl_int) :: i type( fgsl_function ) :: func func = fgsl_function_init( f, c_null_ptr ) i = fgsl_integration_qng ( func, & 0.0_fgsl_double, & 1.0_fgsl_double, & 1e-9_fgsl_double, & 1e-9_fgsl_double, & result, error, neval ) write( *, * ) "Ergebnis =", result ! Ausgabe: ! Ergebnis = 0.946083070367183 end program bsp |
(F)GSL stellt verschiedene Möglichkeiten der numerischen Integration zur Verfügung. Hier wurde die Funktion für den QNG-Algorithmus (non-adaptive Gauss-Kronrod) gewählt.
Beispiel: IEEE-Floating-Point-ArithmetikBearbeiten
Darstellung einer Fließkommazahl nach IEEE 754-Standard:
Beispielsweise wird eine 32-bit-Fließkommazahl binär so aufgegliedert:
seeeeeeeefffffffffffffffffffffff
- s ... sign, 1 bit
- e, E ... exponent, 8 bit (28 = 256; Emin = -127; Emax = 128)
- f ... fraction, 23 bit
Näheres zum IEEE 754-Standard findet sich z.B. bei IEEE 754
FGSL bietet Subroutinen, um Fießkommazahlen anschaulich entsprechend dem IEEE 754-Standard auszugeben:
fgsl_ieee_printf( x )
... Ausgabe der Zahl x im IEEE-Format aufstdout
fgsl_ieee_fprintf( str, x )
... Ausgabe der Zahl x im IEEE-Format.str
ist ein C-Zeiger (C:FILE *
, Fortran:type( c_ptr )
).
C | Fortran |
---|---|
#include <gsl/gsl_ieee_utils.h> int main() { float a = 1.5; double b = -2.6666666666666666; gsl_ieee_printf_float( &a ); gsl_ieee_printf_double( &b ); } |
program bsp use fgsl implicit none real( kind = fgsl_float ) :: a = 1.5 real( kind = fgsl_double ) :: b = -2.6666666666666666d0 call fgsl_ieee_printf( a ) call fgsl_ieee_printf( b ) end program bsp |
Ausgabe: 1.10000000000000000000000*2^0 -1.0101010101010101010101010101010101010101010101010101*2^1 |
Mit der Subroutine fgsl_ieee_env_setup()
lassen sich über die Environment-Variable GSL_IEEE_MODE
einige nützliche Attribute (Rundungsmodus etc.) festlegen.
C | Fortran |
---|---|
#include <stdio.h> #include <gsl/gsl_ieee_utils.h> int main() { float a = 1.5; int i; gsl_ieee_env_setup(); for(i = 0; i < 5; i++) { a /= 0.11; printf("%f\n", a); } } |
program bsp use fgsl implicit none real :: a = 1.5 integer :: i call fgsl_ieee_env_setup() do i = 0, 4 a = a / 0.11 write( *, * ) a end do end program bsp |
Programmaufruf: GSL_IEEE_MODE="round-to-nearest" ./a.out Ausgabe: GSL_IEEE_MODE="round-to-nearest,trap-common" 13.636364 123.966942 1126.972168 10245.201172 93138.195312 |
Programmaufruf: GSL_IEEE_MODE="round-to-nearest" ./a.out Ausgabe: GSL_IEEE_MODE="round-to-nearest,trap-common" 13.636364 123.96695 1126.9723 10245.203 93138.21 |
Programmaufruf: GSL_IEEE_MODE="round-up" ./a.out Ausgabe: GSL_IEEE_MODE="round-up,trap-common" 13.636364 123.966949 1126.972290 10245.203125 93138.210938 |
Programmaufruf: GSL_IEEE_MODE="round-up" ./a.out Ausgabe: GSL_IEEE_MODE="round-up,trap-common" 13.636364 123.96695 1126.9723 10245.203 93138.21 |
Programmaufruf: GSL_IEEE_MODE="round-down" ./a.out Ausgabe: GSL_IEEE_MODE="round-down,trap-common" 13.636363 123.966934 1126.972046 10245.200195 93138.179688 |
Programmaufruf: GSL_IEEE_MODE="round-down" ./a.out Ausgabe: GSL_IEEE_MODE="round-down,trap-common" 13.636363 123.966934 1126.972 10245.2 93138.18 |
WeblinksBearbeiten
LAPACKBearbeiten
AllgemeinesBearbeiten
LAPACK steht für "Linear Algebra Package". LAPACK ist eine Bibliothek zwecks Lösung von
- linearen Gleichungssystemen
- LLS-Aufgaben
- Eigenwertproblemen
- Singulärwertproblemen
LAPACK ist in FORTRAN 77 geschrieben und nutzt weitgehend Funktionen von BLAS Level 3. Falls keine für einen bestimmten Prozessor optimierte Version von BLAS, z.B. ATLAS, bereits installiert ist, kann LAPACK mit der eigenen BLAS-Implementierung kompiliert werden. Aus FORTRAN 77 resultierende Namensbeschränkung auf eine maximale Länge von 6 Zeichen führt zu sehr kryptischen Unterprogrammbezeichnungen, z.B.
D | G | E | T | R | F | |
Zeichenposition: | D1 | M1 | M2 | O1 | O2 | O3 |
Erläuterung der Zeichenpositionen:
D1 | Datentyp:
| ||||||||||||
M1, M2 | Matrixtyp, z.B.
| ||||||||||||
O1, O2, (O3) | Operation, z.B.
|
Eine detailliertere und umfassendere Beschreibung des Funktionsumfanges und der Anwendungsmöglichkeiten der LAPACK-Bibliothek bietet der LAPACK Users' Guide. Die einzelnen Subroutinen inklusive Unterprogrammparameter sind zudem auch in den LAPACK-Sourcecode-Dateien ausführlich dokumentiert.
Beispiel: Lösen eines einfachen GleichungssystemsBearbeiten
Gegeben ist folgendes Gleichungssystem:
bzw. in Matrixschreibweise:
Gesucht sind die Unbekannten x und y:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: info real, dimension(2,2) :: a = reshape ( (/2.,3.,1.,1./), (/2,2/) ) real, dimension(2) :: b = (/5., 6./), ipiv ! SUBROUTINE SGESV(N, NRHS, A, LDA, IPIV, B, LDB, INFO) ! s ... real, ge ... general matrix type, sv ... solver call sgesv(2, 1, a, 2, ipiv, b, 2, info) write(*,*) "Lösung (x, y): ", b if(info == 0) then write(*,*) "Ergebnis OK" else write(*,*) "Ergebnis NOK" end if ! Ausgabe: Lösung (x, y): 1.000000 3.000000 ! Ausgabe: Ergebnis OK end program bsp |
Kompilieren, Linken:
gfortran bsp.f95 -llapack -lblas
Beispiel: Inverse MatrixBearbeiten
Gegeben ist eine 3x3-Matrix
die invertiert werden soll. Die Zahlenwerte dieser Matrix A entsprechen einem Beispiel aus Bartsch: Mathematische Formeln, 21. Auflage, VEB Fachbuchverlag Leipzig, 1986, Seite 109, ebenfalls zum Thema "Inverse Matrix".
Verwendet werden hierzu die beiden LAPACK-Subroutinen:
- SGETRF
- S ... Datentyp: real
- GE ... Matrixtyp: general
- TRF ... Operation: LU-Faktorisierung (Dreiecksform)
- SGETRI
- S ... Datentyp: real
- GE ... Matrixtyp: general
- TRI ... Operation: Invertierung einer LU-faktorisierten Matrix
Fortran 90/95-Code (free source form) |
program bsp implicit none real, dimension( 3, 3 ) :: A integer, dimension( 3 ) :: ipiv real, dimension( 3 ) :: work integer :: m = 3, n = 3, lda = 3, lwork = 3, info A = reshape( (/ 3.0, -3.0, 2.0, -2.0, 5.0, -1.0, 1.0, 0.0, 2.0 /), & shape( A ) ) ! LU-Faktorisierung (Dreieckszerlegung) der Matrix A call sgetrf( m, n, A, lda, ipiv, info ) ! Inverse der LU-faktorisierten Matrix A call sgetri( n, A, lda, ipiv, work, lwork, info ) write( *, * ) "Inverse Matrix Ai =", A write( *, * ) "Testweise wie im Bartsch-Beispiel, Ai = 1/11 * (", A * 11, ")" if( info == 0 ) then write( *, * ) "OK" else write( *, * ) "Nicht OK" end if ! Ausgabe: ! Inverse Matrix Ai = 0.909091 0.54545456 -0.63636374 0.2727273 ! 0.36363637 -0.090909116 -0.45454553 -0.2727273 0.81818193 ! Testweise wie im Bartsch-Beispiel, Ai = 1/11 * ( 10.000001 6. -7.000001 ! 3.0000005 4. -1.0000002 -5.000001 -3.0000005 9.000001 ) ! OK end program bsp |
WeblinksBearbeiten
- LAPACK - Linear Algebra Package
- LAPACK Search Engine
- LAPACK95 - Fortran95 interface to LAPACK
- Erstellen der LAPACK-Bibliothek mit gfortran
Parallele ProgrammierungBearbeiten
OpenMPBearbeiten
Was ist OpenMPBearbeiten
OpenMP ist die Abkürzung für "Open specifications for Multi Processing" und ist eine API für Fortran und C/C++, die zum Zwecke der parallelen Programmierung mittels Shared-Memory-Ansatz für Mehrprozessor-Systeme erschaffen wurde.
Durch Anwendung von Compiler-Direktiven und spezieller Unterprogramme wird die Abarbeitung von bestimmten Programmkonstrukten auf mehrere Threads aufgeteilt.
Der "Master Thread" mit der Nummer 0 ist in einem OpenMP-Programm standardmäßig immer aktiv. Der Programmierer bestimmt im Programmcode, wann eine Gabelung (fork) in mehrere Threads gefordert wird und wann das Ganze wieder in einen einzelnen Thread vereint werden soll (join).
OpenMP wird schon von vielen Fortran-Compilern unterstützt.
Ein einfaches BeispielBearbeiten
Fortran 90/95-Code (free source form) |
program bsp use omp_lib implicit none ! fork !$omp parallel num_threads(3) ! Das nur 1x ausgeben (beim Master Thread) if( omp_get_thread_num() == 0 ) then write( *, * ) 'Insgesamt gibt es ', omp_get_num_threads(), 'Thread(s)' end if ! Das bei jedem Thread ausgeben write( *, * ) 'Thread ', omp_get_thread_num(), 'ist aktiv' ! join !$omp end parallel ! Ausgabe: ! Insgesamt gibt es 3 Thread(s) ! Thread 0 ist aktiv ! Thread 1 ist aktiv ! Thread 2 ist aktiv end program bsp |
Unter Umständen muss das Modul omp_lib
eingebunden werden. Dieses Modul enthält die interface
für die OpenMP-Routinen. Eine mögliche Form des OpenMP-Modus ist am Ende dieses Abschnittes angegeben.
Kompilieren und Linken des Beispielprogramms:
gfortran: |
gfortran -fopenmp -o bsp bsp.f90 |
Intel Fortran Compiler: |
ifort -openmp -o bsp bsp.f90 |
Erläuterung:
- OpenMP-Direktiven werden als Kommentare gekapselt. Bei Verwendung der "free source form" lautet der erste Direktiven-Abschnitt (en. sentinel, dt. Wächter) immer
!$omp
. Groß-/Kleinschreibung spielt keine Rolle. Es folgt die Anweisung, dass sich nun das Programm gabeln soll (parallel
). Die Anzahl der gewünschten Threads wird hier explizit mittels der Optionnum_threads()
festgelegt. - Verwendete OpenMP-Funktionen:
omp_get_thread_num()
... Aktuelle Thread-Nummeromp_get_num_threads()
... Anzahl der Threads
- Beendet wird der parallele Programmteil mit
!$omp end parallel
Thread-Erzeugung: Die parallel
-DirektiveBearbeiten
Wie im vorigen Beispiel bereits angedeutet, wird ein "fork" (die Threaderzeugung) immer mit der Direktive
!$omp parallel [optionen]
eingeleitet und mit
!$omp end parallel
beendet. Es kann hier auch eine Reihe von optionalen Steueranweisungen angegeben werden (siehe vorheriges Beispiel und nachfolgende Beispiele).
Thread-Anzahl bestimmenBearbeiten
- Festlegung im Rahmen der OpenMP-Direktive
!$omp parallel
über die Optionnum_threads( nr )
- Mittels OpenMP-Subroutinenaufruf vor dem fork:
call omp_set_num_threads( nr )
- Festlegung in der Kommandozeile vor Ausführung des Programmes, z.B.:
export OMP_NUM_THREADS=nr
- Default (normalerweise 1 Thread pro CPU)
- Dynamische Anpassung zur Programmlaufzeit per Run-Time-Environment:
call omp_set_dynamic( .true. )
Sichtbarkeit/Gültigkeit von DatenBearbeiten
Aufgrund des Shared-Memory-Ansatzes werden Daten standardmäßig zwischen den Threads geteilt. Dieses Verhalten kann aber auch optional geändert werden. Mögliche Varianten für die parallel
-Direktive:
shared
... Solche Daten sind explizit in allen Threads sichtbar und gültig. Eine Änderung solcher Daten in einem Thread wirkt sich auf alle anderen Threads aus.private
... Solche Daten sind nur im aktuellen Thread sichtbar und gültig, sie werden beim Eintritt in den parallelen Programmabschnitt nicht speziell initialisiert. Änderungen dieser Werte wirken sich nicht auf nachfolgende serielle Programmteile aus.firstprivate
... Ähnlich wieprivate
. Der Unterschied zuprivate
ist, dass solcherart markierte Daten mit dem letztgültigen Wert aus dem vorhergehenden seriellen Programmabschnitt initialisiert werden.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp use omp_lib implicit none integer :: a, b, c, tnr a = 123 b = 123 c = 123 ! Seriell write( *, * ) 'Seriell:' write( *, * ) 'a = ', a write( *, * ) 'b = ', b write( *, * ) 'c = ', c write( *, * ) '-------------------------------' call omp_set_num_threads( 3 ) !$omp parallel shared( a ) private( b ) firstprivate( c ) write( *, * ) 'Parallel:' tnr = omp_get_thread_num() ! Aktuelle Threadnummer if( tnr == 0 ) then a = a + 5 b = b + 5 c = c + 5 end if write( *, * ) 'a = ', a write( *, * ) 'b = ', b write( *, * ) 'c = ', c write( *, * ) '-------------------------------' !$omp end parallel ! Seriell write( *, * ) 'Seriell:' write( *, * ) 'a = ', a write( *, * ) 'b = ', b write( *, * ) 'c = ', c end program bsp |
Ausgabe:
Seriell: a = 123 b = 123 c = 123 ------------------------------- Parallel: 0 a = 128 b = 5 c = 128 ------------------------------- Parallel: 1 a = 128 b = 0 c = 123 ------------------------------- Parallel: 2 a = 128 b = 0 c = 123 ------------------------------- Seriell: a = 128 b = 123 c = 123
Für andere OpenMP-Direktiven sind auch noch andere Sichtbarkeits- und Gültigkeitsbereiche möglich (z.B. lastprivate
).
Die do
-DirektiveBearbeiten
Innerhalb eines parallel
-Blocks können auch do
-Schleifen parallelisiert werden. Die Schleifendurchläufe werden auf die einzelnen Threads bzw. CPUs aufgeteilt.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp use omp_lib implicit none integer :: i, tnr call omp_set_num_threads( 3 ) !$omp parallel private( i ) !$omp do do i = 1, 20 tnr = omp_get_thread_num() ! Aktuelle Threadnummer write( *, * ) 'Thread', tnr, ':', i end do !$omp end do !$omp end parallel end program bsp |
Ausgabe:
Thread 0 : 1 Thread 0 : 2 Thread 0 : 3 Thread 0 : 4 Thread 0 : 5 Thread 0 : 6 Thread 0 : 7 Thread 1 : 8 Thread 1 : 9 Thread 1 : 10 Thread 1 : 11 Thread 1 : 12 Thread 1 : 13 Thread 1 : 14 Thread 2 : 15 Thread 2 : 16 Thread 2 : 17 Thread 2 : 18 Thread 2 : 19 Thread 2 : 20
Die Zuweisung der Schleifendurchläufe an die Threads kann gesteuert werden. Dazu wird der do
-Direktive eine schedule
-Anweisung mit dem Argument Typ und ev. auch mit dem Argument Chunk-Größe beigefügt. Als Typen sind möglich
static
dynamic
guided
runtime
Diese Bezeichnungen beziehen sich auf die Art der Thread-Erzeugung.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp use omp_lib implicit none integer :: i, tnr call omp_set_num_threads( 3 ) !$omp parallel private( i ) !$omp do schedule(static, 3) do i = 1, 20 tnr = omp_get_thread_num() ! Aktuelle Threadnummer write( *, * ) 'Thread', tnr, ':', i end do !$omp end do !$omp end parallel end program bsp |
Ausgabe:
Thread 0 : 1 Thread 0 : 2 Thread 0 : 3 Thread 0 : 10 Thread 0 : 11 Thread 0 : 12 Thread 0 : 19 Thread 0 : 20 Thread 1 : 4 Thread 1 : 5 Thread 1 : 6 Thread 1 : 13 Thread 1 : 14 Thread 1 : 15 Thread 2 : 7 Thread 2 : 8 Thread 2 : 9 Thread 2 : 16 Thread 2 : 17 Thread 2 : 18
Eine do while
-Schleife kann nicht auf diese Art und Weise mittels OpenMP-Direktive parallel ausgeführt werden.
Die sections
-DirektiveBearbeiten
Auch die Festlegung, dass bestimmte Programmabschnitte auf je einen Thread verteilt werden sollen, ist möglich. Dazu wird das Konstrukt
!$omp sections [optionen] !$omp section block !$omp section block ... !$omp end sections
innerhalb eines parallel
-Blocks eingesetzt.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp use omp_lib implicit none integer :: a, b a = 20 b = 30 call omp_set_num_threads( 3 ) !$omp parallel shared( a, b ) !$omp sections !$omp section write( *, * ) omp_get_thread_num(), a write( *, * ) omp_get_thread_num(), "---" !$omp section write( *, * ) omp_get_thread_num(), b write( *, * ) omp_get_thread_num(), "----" !$omp end sections !$omp end parallel ! Ausgabe (ifort): ! 0 20 ! 0 --- ! 1 30 ! 1 ---- end program bsp |
Weitere DirektivenBearbeiten
workshare
single
Kombinierte DirektivenBearbeiten
Unmittelbar aufeinanderfolgende Einzeldirektiven können auch in einer einzigen Direktive zusammengefasst werden. Möglich sind
parallel do
parallel sections
parallel workshare
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp use omp_lib implicit none integer :: i, tnr call omp_set_num_threads( 3 ) !$omp parallel do private(i) schedule(static, 3) do i = 1, 20 tnr = omp_get_thread_num() ! Aktuelle Threadnummer write( *, * ) 'Thread', tnr, ':', i end do !$omp end parallel do end program bsp |
Eine zusammengehörende OpenMP-Direktive darf auch auf mehrere Zeilen verteilt werden.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp use omp_lib implicit none integer :: i, tnr call omp_set_num_threads( 3 ) !$omp parallel do & !$omp private(i) & !$omp schedule(static, 3) do i = 1, 20 tnr = omp_get_thread_num() ! Aktuelle Threadnummer write( *, * ) 'Thread', tnr, ':', i end do !$omp end parallel do end program bsp |
SynchronisationBearbeiten
Bei der parallelen Programmierung können Situationen auftreten, die bei einer seriellen Programmausführung nie passieren würden, z.B. race conditions. Damit es nicht soweit kommt, bietet OpenMP einige Direktiven zur Synchronisation der Threadausführung.
master
-DirektiveBearbeiten
!$omp master ... !$omp end master
Der eingeschlossene Programmblock wird nur vom Master Thread ausgeführt und von den anderen Threads ignoriert.
critical
-DirektiveBearbeiten
!$omp critical ... !$omp end critical
Dieser Programmteil wird zwar von allen Threads ausgeführt, allerdings ist sichergestellt, dass dies nicht gleichzeitig erfolgt.
atomic
-DirektiveBearbeiten
!$omp atomic
Ähnlich zu critical
. Allerding gilt dies Direktive nur für eine einzelne unmittelbar nachfolgende spezielle Programmanweisung.
barrier
-DirektiveBearbeiten
!$omp barrier
Sobald ein Thread eine solche Barriere erreicht, wartet er bis alle andere Threads diese Barriere auch erreicht haben. Erst dann geht's weiter.
flush
-DirektiveBearbeiten
!$omp flush
Erstellung eines konsistenten Speicherbildes.
ordered
-DirektiveBearbeiten
!$omp do ordered do ... ... !$omp ordered ... !$omp end ordered ... end do !$omp end do
"Geordnete Ausführung" von do
-Schleifen in der gleichen Reihenfolge einer seriellen Abarbeitung.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp use omp_lib implicit none integer :: i, tnr call omp_set_num_threads( 3 ) !$omp parallel private( i ) !$omp do ordered schedule(static, 3) do i = 1, 20 !$omp ordered tnr = omp_get_thread_num() ! Aktuelle Threadnummer write( *, * ) 'Thread', tnr, ':', i !$omp end ordered end do !$omp end do !$omp end parallel end program bsp |
Ausgabe:
Thread 0 : 1 Thread 0 : 2 Thread 0 : 3 Thread 1 : 4 Thread 1 : 5 Thread 1 : 6 Thread 2 : 7 Thread 2 : 8 Thread 2 : 9 Thread 0 : 10 Thread 0 : 11 Thread 0 : 12 Thread 1 : 13 Thread 1 : 14 Thread 1 : 15 Thread 2 : 16 Thread 2 : 17 Thread 2 : 18 Thread 0 : 19 Thread 0 : 20
Das Modul omp_lib
Bearbeiten
Das Modul omp_lib
enthält die interface
für die Routinen von OpenMP. Eine mögliche Form des Modules ist nachfolgend abgebildet. In dem Modul wird die import
-Anweisung verwendet, die Teil des Standards Fortran 2003 ist.
Fortran 90/95-Code (free source form) |
module omp_lib ! ! OpenMP Fortran API v2.5 ! implicit none integer, parameter, private :: sgl = kind( 0.0 ) integer, parameter, private :: dbl = kind( 0.0d0 ) integer, parameter, private :: omp_real_kind = dbl integer, parameter, private :: omp_integer_kind = sgl integer, parameter, private :: omp_logical_kind = sgl integer, parameter, private :: omp_lock_kind = dbl integer, parameter, private :: omp_nest_lock_kind = dbl interface subroutine omp_destroy_lock ( var ) import :: omp_lock_kind integer ( kind=omp_lock_kind ), intent(inout) :: var end subroutine omp_destroy_lock subroutine omp_destroy_nest_lock ( var ) import :: omp_nest_lock_kind integer ( kind=omp_nest_lock_kind ), intent(inout) :: var end subroutine omp_destroy_nest_lock function omp_get_dynamic () import :: omp_logical_kind logical ( kind=omp_logical_kind ) :: omp_get_dynamic end function omp_get_dynamic function omp_get_max_threads () import :: omp_integer_kind integer ( kind=omp_integer_kind ) :: omp_get_max_threads end function omp_get_max_threads function omp_get_nested () import :: omp_logical_kind logical ( kind=omp_logical_kind ) :: omp_get_nested end function omp_get_nested function omp_get_num_procs () import :: omp_integer_kind integer ( kind=omp_integer_kind ) :: omp_get_num_procs end function omp_get_num_procs function omp_get_num_threads () import :: omp_integer_kind integer ( kind=omp_integer_kind ) :: omp_get_num_threads end function omp_get_num_threads function omp_get_thread_num () import :: omp_integer_kind integer ( kind=omp_integer_kind ) :: omp_get_thread_num end function omp_get_thread_num function omp_get_wtick () import :: omp_real_kind real ( kind=omp_real_kind ) :: omp_get_wtick end function omp_get_wtick function omp_get_wtime () import :: omp_real_kind real ( kind=omp_real_kind ) :: omp_get_wtime end function omp_get_wtime subroutine omp_init_lock ( var ) import :: omp_lock_kind integer ( kind=omp_lock_kind ), intent(out) :: var end subroutine omp_init_lock subroutine omp_init_nest_lock ( var ) import :: omp_nest_lock_kind integer ( kind=omp_nest_lock_kind ), intent(out) :: var end subroutine omp_init_nest_lock function omp_in_parallel () import :: omp_logical_kind logical ( kind=omp_logical_kind ) :: omp_in_parallel end function omp_in_parallel subroutine omp_set_dynamic ( enable_expr ) import :: omp_logical_kind logical ( kind=omp_logical_kind ), intent(in) :: enable_expr end subroutine omp_set_dynamic subroutine omp_set_lock ( var ) import :: omp_lock_kind integer ( kind=omp_lock_kind ), intent(inout) :: var end subroutine omp_set_lock subroutine omp_set_nest_lock ( var ) import :: omp_nest_lock_kind integer ( kind=omp_nest_lock_kind ), intent(inout) :: var end subroutine omp_set_nest_lock subroutine omp_set_nested ( enable_expr ) import :: omp_logical_kind logical ( kind=omp_logical_kind ), intent(in) :: enable_expr end subroutine omp_set_nested subroutine omp_set_num_threads ( number_of_threads_expr ) import :: omp_integer_kind integer ( kind=omp_integer_kind ), intent(in) :: number_of_threads_expr end subroutine omp_set_num_threads function omp_test_lock ( var ) import :: omp_logical_kind, omp_lock_kind logical ( kind=omp_logical_kind ) :: omp_test_lock integer ( kind=omp_lock_kind ), intent(inout) :: var end function omp_test_lock function omp_test_nest_lock ( var ) import :: omp_integer_kind, omp_nest_lock_kind integer ( kind=omp_integer_kind ) :: omp_test_nest_lock integer ( kind=omp_nest_lock_kind ), intent(inout) :: var end function omp_test_nest_lock subroutine omp_unset_lock ( var ) import :: omp_lock_kind integer ( kind=omp_lock_kind ), intent(inout) :: var end subroutine omp_unset_lock subroutine omp_unset_nest_lock ( var ) import :: omp_nest_lock_kind integer ( kind=omp_nest_lock_kind ), intent(inout) :: var end subroutine omp_unset_nest_lock end interface end module omp_lib |
LiteraturBearbeiten
- Rohit Chandra, Leonardo Dagum, Dave Kohr, Dror Maydan, Jeff McDonald, Ramesh Menon, Parallel Programming in OpenMP, Morgan Kaufmann Publishers, 2001, ISBN-13: 978-1-55860-671-5, ISBN-10: 1-55860-671-8
WeblinksBearbeiten
Fortran und TclBearbeiten
Tcl/Tk kann im Zusammenhang mit Fortran zwecks Erstellung einer Tk-Benutzeroberfläche für Fortran-Programme interessant sein. Die zeitkritischen oder mathematisch orientierten Programmteile werden mittels Fortran-Code realisiert. Der Programmcode für die Benutzerschnittstelle wird mittels Tcl/Tk-Skript zur Verfügung gestellt.
BeispielBearbeiten
PrinzipskizzeBearbeiten
Tcl/Tk-CodeBearbeiten
#!/usr/bin/wish
wm title . Sinus ;# Fenstertitel
entry .e1 ;# Eingabefeld
button .b1 -text "Hier drücken" -command fcall ;# Schaltfläche
label .l1 -bg green ;# Textfeld
pack .e1 -padx 10 -pady 5 ;# Widgets packen
pack .b1 -padx 10 -pady 5
pack .l1 -padx 10 -pady 5
proc fcall { } { ;# Kommunikation mit Fortran, Ergebnis schreiben
set f [open "|./a.out" r+] ;# a.out ist das kompilierte und gelinkte Fortran-Programm
set val [.e1 get]
puts $f $val
flush $f
gets $f wert
close $f
.l1 config -text $wert
}
Fortran-CodeBearbeiten
Fortran 90/95-Code (free source form) |
program bsp implicit none real :: val, sin read (*,*) val write(*,'(A12,F6.3)') "Ergebnis = ", sin(val) end |
ProgrammausführungBearbeiten
Das Fortran-Programm muss selbstverständlich vorab einmal kompiliert und gelinkt werden. Im Beispielsfall muss die exekutierbare Ausgabedatei a.out heißen. Unter Linux wird das Tcl-Skript vor dem ersten Start als ausführbar (-> mittels chmod-Befehl) markiert. Der Programmaufruf erfolgt über das Tcl-Skript, das wie ein normales Programm durch Eingabe des Programmnamens gestartet wird.
ErgebnisBearbeiten
AlternativenBearbeiten
Tcl/Tk bietet eine Schnittstelle zur Programmiersprache C (tcl.h, tk.h, libtcl*.so, libtk*.so
). Mit dem im nächsten Kapitel behandelten Fortran-C-Binding kann auf diese C-Funktionen zugegriffen werden. Die Ftcl-Bibliothek nutzt diesen Mechanismus.
WeblinksBearbeiten
- Tcl/Tk-Homepage
- Programming:Tcl (englischsprachiges Wikibook)
- Ftcl: Using Tcl in Fortran programs and vice versa
Fortran und CBearbeiten
InterfaceBearbeiten
Ein Interface ist die Schnittstellendefinition zu einem externen Unterprogramm. Das externe Unterprogramm kann, muss aber nicht in Fortran geschrieben sein.
interface Spezifikation der externen Unterprogramme end interface |
Beispiel:
interface ! subroutine x(a, b, c) sei ein externes Unterprogramm subroutine x(a, b, c) real, intent(in) :: a integer :: b, c end subroutine x end interface call x(2.5, 1, 3)
Fortran 90/95Bearbeiten
In Fortran 90/95 ist der Zugriff auf C-Funktionen nicht standardisiert. Derartige Zugriffe sind compilerabhängig zu lösen. Es sind je nach verwendetem Fortran-Compiler verschiedene Compilerdirektiven zu setzen, Optionen beim Compileraufruf und ähnliches zu beachten. Fortran 2003 bietet eine standardisierte Schnittstelle für den Zugriff auf C-Funktionen.
g95 (gfortran) und gccBearbeiten
Beispiel: "call by value" und "call by reference"Bearbeiten
Fortran übergibt die Argumente eines Unterprogrammes standardmäßig "call by reference". Im Zusammenspiel mit C besteht nun das Problem, dass in C Argumente "call by reference" via Pointer oder "call by value" übergeben werden . In g77, g95 und gfortran ist die Funktion %val inkludiert, mit der auch in Fortran der "call by value"-Mechanismus nachgebildet werden kann.
Fortran-Code bsp.f90:
Fortran 90/95-Code (free source form) |
program bsp implicit none interface subroutine zahl(a, b) integer :: a, b end subroutine zahl end interface call zahl(%val(5), 7) end program bsp |
C-Code bsp.c:
Programmcode |
#include <stdio.h> void zahl(int a, int *b) { printf("%s%d\n", "Ergebnis = ", a * *b); } |
Compilieren und Linken:
gcc -c -o bsp1.o bsp.c g95 -c -fno-underscoring -o bsp2.o bsp.f90 g95 bsp1.o bsp2.o
Ausgabe:
Ergebnis = 35
Beispiel: Übergabe von ZeichenkettenBearbeiten
In C sind Strings im Gegensatz zu Fortran Null-terminiert. Dies muss beim Aufruf einer C-Prozedur aus Fortran berücksichtigt werden.
Fortran Code bsp.f90:
Fortran 90/95-Code (free source form) |
program bsp implicit none interface subroutine hallo(str) character(*) :: str end subroutine end interface call hallo("Hallo, Sonne" // char(0)) ! char(0) -> Nulltermination für C call hallo("Hallo, Protuberanz" // char(0)) ! char(0) -> Nulltermination für C call hallo("Hallo, Mond") ! keine Nulltermination ! Ausgabe: ! Hallo, Sonne ! Hallo, Protuberanz ! Hallo, Mond ... More segments remain end program bsp |
C-Code bsp.c:
Programmcode |
#include <stdio.h> void hallo(char *str) { printf("%s\n", str); } |
Compilieren und Linken:
gcc -c -o bsp1.o bsp.c g95 -c -fno-underscoring -o bsp2.o bsp.f90 g95 bsp1.o bsp2.o
Beispiel: RückgabewertBearbeiten
Die Rückgabe eines Wertes einfachen Datentyps aus einer C-Funktion nach Fortran stellt kein Problem dar. Es ist einzig zu beachten, dass der Datentyp in C und Fortran übereinstimmt.
Fortran-Code bsp.f90:
Fortran 90/95-Code (free source form) |
program bsp implicit none interface function zahl(x, y) integer :: zahl integer :: x, y end function end interface integer :: res res = zahl(%val(76), %val(32)) write(*,*) res ! Ausgabe: 108 end program bsp |
C-Code bsp.c:
Programmcode |
int zahl(int a, int b) { return (a+b); } |
ifort und gccBearbeiten
Beispiel: "call by value" und "call by reference"Bearbeiten
Fortran-Code bsp.f90:
Fortran 90/95-Code (free source form) |
program bsp implicit none interface subroutine zahl(a, b) !dec$ attributes c :: zahl !dec$ attributes reference :: b integer :: a, b end subroutine zahl end interface call zahl(5, 7) end program bsp |
C-Code bsp.c:
Programmcode |
#include <stdio.h> void zahl(int a, int *b) { printf("%s%d\n", "Ergebnis = ", a * *b); } |
Compilieren, Linken:
gcc -c -o bsp1.o bsp.c ifort -c -o bsp2.o bsp.f90 ifort bsp1.o bsp2.o
Ausgabe:
Ergebnis = 35
Beispiel: Übergabe von ZeichenkettenBearbeiten
Fortran-Code bsp.f90:
Fortran 90/95-Code (free source form) |
program bsp implicit none interface subroutine hallo(str) !dec$ attributes c :: hallo !dec$ attributes reference :: str character(*) :: str end subroutine end interface call hallo("Hallo, Sonne" // char(0)) ! char(0) -> Nulltermination call hallo("Hallo, Protuberanz"C) ! C -> Nulltermination (bei Intel-Fortran-Compiler) call hallo("Hallo, Mond") ! keine explizite Nullterminiation ! Ausgabe: ! Hallo, Sonne ! Hallo, Protuberanz ! Hallo, Mond end program bsp |
C-Code bsp.c:
Programmcode |
#include <stdio.h> void hallo(char *str) { printf("%s\n", str); } |
Compilieren und Linken:
gcc -c -o bsp1.o bsp.c ifort -c -o bsp2.o bsp.f90 ifort bsp1.o bsp2.o
Beispiel: RückgabewertBearbeiten
Fortran-Code bsp.f90:
Fortran 90/95-Code (free source form) |
program bsp implicit none interface function zahl(x, y) !dec$ attributes c :: zahl integer :: zahl integer :: x, y end function end interface integer :: res res = zahl(76, 32) write(*,*) res ! Ausgabe: 108 end program bsp |
C-Code bsp.c:
Fortran 90/95-Code (free source form) |
int zahl(int a, int b) { return (a+b); } |
Fortran 2003Bearbeiten
In Fortran 2003 ist es viel einfacher auf C zu zugreifen, als in Fortran 95. Es wurde im Fortran 2003-Standard ein intrinsisches Modul namens iso_c_binding
vorgesehen, das die zum Zugriff auf C-Programme nötigen Elemente enthält.
Ein einfaches BeispielBearbeiten
Beispiel funktioniert mit Compiler
- g95 (0.91!) May 10 2007: ja
- gfortran 4.3.0 20070723 (experimental): ja
- Intel Fortran Compiler 10.0: ja
- Sun Studio Express - June 2007: ja
Anmerkungen:
Fortran 2003-Code: bsp.f95
Fortran 2003-Code |
program bsp implicit none interface function addition( a, b ) bind( c[, name="c_func"] ) use, intrinsic :: iso_c_binding real( kind = c_float ), value :: a real( kind = c_float ), value :: b real( kind = c_float ) :: addition end function addition end interface write (*,*) addition( 2.5, 3.3 ) ! Ausgabe: 5.8 end program bsp |
C-Code: bsp.c
Programmcode |
float addition(float a, float b) { return (a + b); } |
Makefile:
Programmcode |
FC = g95 # oder gfortran, ... CC = gcc # oder icc, ... bsp: bsp_c.o bsp_f95.o $(FC) -o bsp bsp_c.o bsp_f95.o bsp_c.o: bsp.c $(CC) -c -o bsp_c.o bsp.c bsp_f95.o: bsp.f95 $(FC) -c -o bsp_f95.o bsp.f95 .PHONY: clean clean: rm *.o |
Was ist neu gegenüber Fortran 95?
|
... | bind-Attribut, stellt unter anderem die Interoperabilität mit C bezüglich Prozedurnamenskonventionen nach der Übersetzung sicher. Mittels des optionalen Arguments "name" kann die Funktion in Fortran umbenannt werden. "c_func" ist dabei der Name der Funktion in C. |
|
... | Einbindung des intrinsischen Moduls iso_c_binding
|
|
... | C-Datentyp float .
|
|
... | call by value |
Datentyp-ZuordnungBearbeiten
Das iso_c_binding
-Modul stellt benannte Konstanten zur Verfügung, die bei Fortran-Datentypen als kind-Wert zu verwenden sind, um den jeweiligen C-Datentyp zu charakterisieren. Weist eine solche Konstante einen negativen Wert auf, dann ist keine Entsprechung von Fortran-Datentyp zu C-Datentyp vorhanden.
Fortran-Datentyp | Benannte iso_c_binding -Konstante (kind-Wert)
|
C-Datentyp |
---|---|---|
integer | c_int | int |
c_short | short int | |
c_long | long int | |
c_long_long | long long int | |
c_signed_char | signed char, unsigned char | |
c_size_t | size_t | |
c_int8_t | int8_t | |
c_int16_t | int16_t | |
c_int32_t | int32_t | |
c_int64_t | int64_t | |
c_int_least8_t | int_least8_t | |
c_int_least16_t | int_least16_t | |
c_int_least32_t | int_least32_t | |
c_int_least64_t | int_least64_t | |
c_int_fast8_t | int_fast8_t | |
c_int_fast16_t | int_fast16_t | |
c_int_fast32_t | int_fast32_t | |
c_int_fast64_t | int_fast64_t | |
c_intmax_t | intmax_t | |
c_intptr_t | intptr_t | |
real | c_float | float |
c_double | double | |
c_long_double | long double | |
complex | c_float_complex | float _Complex |
c_double_complex | double _Complex |