Programmieren in Fortran 90/95


  1. Erweiterte Datentypen
  2. Der intrinsische Datentyp complex

    Genaugenommen handelt es sich hier um einen in Fortran generisch enthaltenen Datentyp für komplexe Zahlen. Diese haben in der Mathematik meist die Form

            c = a + i b

    wobei sich die komplexe Zahl c aus der Menge der komplexen Zahlen aus dem Realteil a und dem Imaginärteil b zusammensetzt. Zur Darstellung komplexer Zahlen wird eine Ebene benötigt. Ein komplexer Zahlenwert ist dann ein Punkt in der komplexen Ebene. An der Achse nach rechts (der Abzisse) lässt sich der Realteil und auf der Ordinate der Imaginärteil ablesen. Der Betrag einer komplexen Zahl ist der Abstand des Punktes vom Ursprung des Koordinatensystems und berechnet sich zu

            |c| = sqrt(a*a+b*b)

    Der Phasenwinkel phi ist gegeben durch den Arcustangens von b/a.

            phi = atan(b/a)

    Komplexe Zahlen lassen sich addieren, subtrahieren, multiplizieren und dividieren. Allerdings sind komplexe Zahlen nicht geordnet, d.h. ein Vergleich, ob die komplexe Zahl c1 kleiner oder größer als die komplexe Zahl c2 ist, ist sinnlos. Was sich allerdings wieder vergleichen ließe, ist, ob |c1| kleiner oder größer als |c2| ist, weil der Betrag einer komplexen Zahl wieder eine reelle Zahl ist und weil im Vergleich zweier reeller Zahlen unterschieden werden kann, welche der Zahlen die größere von beiden ist.

    Komplexe Zahlen werden in Fortran wie folgt dargestellt:

    komplexe Zahl (math.) Fortran-Darstellung
    3 + 4i (3.0,4.0) bzw. (3.,4.)
        -i (0.0,-1.0)
        1 (1.0,0.0)
    -0.0042+ 75i  z.B.(-4.2E-3,0.75E2)

    Deklaration komplexer Variablen

    Komplexe Variablen werden als Datentyp complex deklariert, z.B. eine komplexe Zahl c

          complex :: c
    
    oder z.B. ein Datenfeld feldmit 10 komplexen Komponenten mit
          complex, dimension(10) :: feld
    
    Wertzuweisungen bei der Deklaration der Variablen lassen sich ebenfalls durchführen, wenn z.B. die Konstante i immer als komplexe Zahl i geführt werden soll, kann man dies gut mit
          complex, parameter :: i = (0.0,1.0)
    
    durchführen. Danach lassen sich die komplexe Variablen mit der vordeklarierten komplexen Zahl i gut initialisieren. Und i lässt sich als komplexe Zahl i im Programmcode gut einsetzen.

    Beispiel: complex_demo.f90

    program complex_demo 
       implicit none
       complex, parameter    :: i  = (0.0,1.0)
       complex               :: z1 = 3.0 + 4*i    ! interne Datentypkonversion
       complex               :: z2 = (-2.0,1.0)
       complex, dimension(5) :: feld
       integer               :: n  
    
       write(*,*) 'Demoprogramm mit komplexen Zahlen'
       write(*,*)
       write(*,*) 'voreingestellt sind: '
       write(*,*)  
       write(*,*) '    i = ', i
       write(*,*) '   z1 = ', z1
       write(*,*) '   z2 = ', z2
       write(*,*) 
       write(*,*) 'Arithmetik mit komplexen Zahlen:'
       write(*,*)
       write(*,*) ' 5         * i          = ', 5*i
       write(*,*) ' 5         * z1         = ', 5.0*z1
       write(*,*) ' 5.0       * z1         = ', 5.0*z1
       write(*,*) ' (5.0,0.0) * z1         = ', (5.0,0.0)*z1
       write(*,*)
       write(*,*) '        z1 + z2         = ', z1 + z2
       write(*,*) '        z1 - z2         = ', z1 - z2
       write(*,*) '        z1 * z2         = ', z1 * z2
       write(*,*) '        z1 / z2         = ', z1 / z2
       write(*,*)  
       write(*,*) 'Der Betrag einer komplexen Zahl:'
       write(*,*) '        cabs(z1)        = ', cabs(z1)
       write(*,*) '         abs(z2)        = ',  abs(z2)
       write(*,*) 'Der Realteil einer komplexen Zahl :'
       write(*,*) '        real(z1)        =', real(z1) 
       write(*,*) '        real(z2)        =', real(z2) 
       write(*,*) '        real(i)         =', real(i) 
       write(*,*) 'Der Imaginaerteil einer komplexen Zahl:' 
       write(*,*) '        aimag(z1)        =', aimag(z1) 
       write(*,*) '        aimag(z2)        =', aimag(z2) 
       write(*,*) '        aimag(i)         =', aimag(i) 
       write(*,*) 'Das Komplex-Konjugierte einer komplexen Zahl:'
       write(*,*) '        conjg(z1)        =', conjg(z1)
       write(*,*) '        conjg(z2)        =', conjg(z2)
       write(*,*) '        conjg(i)         =', conjg(i)
       write(*,*) 
       write(*,*) 'Zwei real-Zahlen zu einer komplexen Zahl &
                  &zusammensetzen mit cmplx'
       write(*,*) '        cmplx(2.5,7.1)   =', cmplx(2.5,7.1)
       write(*,*) 
       write(*,*) 'Verarbeitung von komplexwertigen Datenfeldern, z.B mit:'
       write(*,*) '      do n=1, 5'
       write(*,*) '          feld(n) = cmplx(real(n),-real(n*n))' 
       write(*,*) '          write(*,*) feld(n)'
       write(*,*) '      end do'
       write(*,*)
                         do n=1, 5
                             feld(n) = cmplx(real(n),-real(n*n))
                             write(*,*) feld(n)
                         end do
       write(*,*)
       write(*,*) 'Anwenden mathematischer Funktionen auf komplexe Zahlen, z.B. '
       write(*,*) '         log(i)           =', log(i)     
       write(*,*) '         log(z1)          =', log(z1)     
       write(*,*) '         log(z2)          =', log(z2)     
       write(*,*)  
       write(*,*) 'formatierte Ausgabe von komplexen Zahlen:'
       write(*,*) 'im Formatbeschreiber wird eine komplexe Zahl'
       write(*,*) 'wie 2 aufeinanderfolgende real-Zahlen behandelt:'
       write(*,*) 
       write(*,'(2F5.1)') 1.5+2.0*i 
    
    end program complex_demo 
    

    Bildschirmausgabe: complex_demo.erg
     Demoprogramm mit komplexen Zahlen
     
     voreingestellt sind: 
     
         i =  (0.0000000E+00,1.000000)
        z1 =  (3.000000,4.000000)
        z2 =  (-2.000000,1.000000)
     
     Arithmetik mit komplexen Zahlen:
     
      5         * i          =  (0.0000000E+00,5.000000)
      5         * z1         =  (15.00000,20.00000)
      5.0       * z1         =  (15.00000,20.00000)
      (5.0,0.0) * z1         =  (15.00000,20.00000)
     
             z1 + z2         =  (1.000000,5.000000)
             z1 - z2         =  (5.000000,3.000000)
             z1 * z2         =  (-10.00000,-5.000000)
             z1 / z2         =  (-0.4000000,-2.200000)
     
     Der Betrag einer komplexen Zahl:
             cabs(z1)        =    5.000000    
             cabs(z2)        =    2.236068    
     Der Realteil einer komplexen Zahl :
             real(z1)        =   3.000000    
             real(z2)        =  -2.000000    
             real(i)         =  0.0000000E+00
     Der Imaginaerteil einer komplexen Zahl:
             aimag(z1)        =   4.000000    
             aimag(z2)        =   1.000000    
             aimag(i)         =   1.000000    
     Das Komplex-Konjugierte einer komplexen Zahl:
             conjg(z1)        = (3.000000,-4.000000)
             conjg(z2)        = (-2.000000,-1.000000)
             conjg(i)         = (0.0000000E+00,-1.000000)
    
     Zwei real-Zahlen zu einer komplexen Zahl zusammensetzen mit cmplx
             cmplx(2.5,7.1)   = (2.500000,7.100000)
     
     Verarbeitung von komplexwertigen Datenfeldern, z.B mit:
           do n=1, 5
               feld(n) = cmplx(real(n),-real(n*n))
               write(*,*) feld(n)
           end do
     
     (1.000000,-1.000000)
     (2.000000,-4.000000)
     (3.000000,-9.000000)
     (4.000000,-16.00000)
     (5.000000,-25.00000)
     
     Anwenden mathematischer Funktionen auf komplexe Zahlen, z.B. 
              log(i)           = (0.0000000E+00,1.570796)
              log(z1)          = (1.609438,0.9272952)
              log(z2)          = (0.8047190,2.677945)
     
     formatierte Ausgabe von komplexen Zahlen:
     im Formatbeschreiber wird eine komplexe Zahl
     wie 2 aufeinanderfolgende real-Zahlen behandelt:
    
      1.5  2.0
    

    Die komplexen Zahlen werden im Rechner von der Voreinstellung her sowohl im Realteil als auch im Imaginärteil mit der Genauigkeit des Datentyps real verarbeitet.

    Einlesen komplexer Werte

    Beispiel: read_complex.f90

    program read_complex    
       implicit none
       complex :: z
       
       do
         write(*,*) 'Geben Sie eine komplexe Zahl ein!'
         read(*,*) z
         write(*,*) 'eingelesen wurde: ', z
         write(*,*) 
       end do
       
    end program read_complex    
    

    Anwendungs-Beispiel mit dem CVF v6.6: read_complex.erg

     Geben Sie eine komplexe Zahl ein!
    (2.0,3.7)
     eingelesen wurde:  (2.000000,3.700000)
     
     Geben Sie eine komplexe Zahl ein!
    2.0 4.5
     eingelesen wurde:  (2.000000,3.700000)
     
     Geben Sie eine komplexe Zahl ein!
    (1.0,2.3)
     eingelesen wurde:  (1.000000,2.300000)
    ...
    (die weitere Ausführung wurde mit CTRL-C abgesprochen)
    

    Achtung: der Salford FTN95 erwartet beim listengesteuerten Einlesen komplexer Zahlen den Real- und Imaginärteil in runde Klammern eingeschlossen und durch ein Komma getrennt, sonst erhält man einen Programmabbruch mit einem Runtime-Error.

    Um das Einleseprogramm allgemeingültig und portabel zu gestalten, ist somit das Programm um eine Fehlerabfang-Routine zu erweitern ( read_complex_erweitert.f90):

    program read_complex_erweitert    
       implicit none
       complex :: z
       integer :: io_err
       
       do
         write(*,*) 'Geben Sie eine komplexe Zahl ein!'
         read(*,*,iostat=io_err) z
         if (io_err /= 0) then
           write(*,*) 'Bitte geben Sie die Zahl im Format'
           write(*,*) '(a,b)'
           write(*,*) 'ein, wobei a der Zahlenwert fuer den Realteil und'
           write(*,*) 'b der Zahlenwert fuer den Imaginaerteil ist,'
           write(*,*) 'einschliesslich der Klammern und des Kommas'
           write(*,*)
           cycle
         end if
         write(*,*) 'eingelesen wurde: ', z
         write(*,*) 
       end do
    end program read_complex_erweitert    
    

    Datentypen mit höherer Genauigkeit

    Sowohl der Datentyp real als auch der der Realteil und der Imaginärteil des Datentyps complex werden auf den meisten Compileren intern mit 4 Byte - Darstellungsgenauigkeit codiert. Damit weisen diese beiden Zahlentypen maximal 6-7 Dezimalstellen Genauigkeit auf.

    Da viele wissenschaftliche, technische und numerische Anwendungen nach höherer Darstellungsgenauigkeit verlangen, bietet Fortran 90 eine Möglichkeit die Anzahl an Genauigkeitsstellen zu erhöhen.

    Alle Fortran 90/95 - Compiler bieten die Möglichkeit, ohne größeren Aufwand die Anzahl der Genauigkeitsstellen bei reellen und/oder komplexen Zahlen auf ca. 14-15 Genauigkeitsstellen zu erhöhen. In diesem Fall werden in der Regel vom Compiler für die interne Codierung reeller Zahlen mindestens die doppelte Anzahl an Bytes (4 statt 8 Byte) verwendet.

    Eine übersichtliche, wenn auch nicht die modernste Art, die Anzahl der Genauigkeitsstellen für reelle Zahlen zu erhöhen, besteht darin, die bisherige FORTRAN 77 - Konvention zu nutzen

    "Doppelte" Genauigkeit (auf ca. 14-15 Stellen) für reelle Zahlen:

    1. statt real als Datentyp double precision oder (veraltet) real*8 angeben, z.B.
           double precision :: r, umfang
           
    2. Ausnahmslos müssen gleichzeitig alle(!) Zahlenwerte auf ca. 14-15 Stellen genau angegeben werden und mit dem Zusatz d0 versehen werden, bzw. es muss der Exponent statt mit e mit d eingeleitet werden, z.B.
           double precision,  parameter :: pi = 3.14159265358979d0
               umfang = 2.0d0 * pi * r
           
      Dies bedeutet insbesondere: Wenn Sie die Genauigkeit in der Zahlendarstellung erhöhen wollen, müssen Sie wirklich konsequent alle (!) Zahlenwerte in dem gesamten Programm anpassen, sonst kann es zur Reduktion von den erwünschten ca. 14-15 Stellen auf 6-7 Stellen Genauigkeit kommen!

      Notwendige Umstellungen für double precision

      • bei allen Zahlen (in wissenschaftlicher Darstellung) muss die Exponentenmarkierung e durch d oder D bzw. E durch d oder D ersetzt werden.
        Z.B. statt
                   1.23e4
        
        muss für double precision geschrieben werden
                   1.23d4
        

      • reelle Zahlenwerte in doppelter Genauigkeit ohne Exponentendarstellung müssen zusätzlich mit d0 oder D0 ergänzt werden
        Z.B. statt
                   4.2
        
        muss für double precision geschrieben werden
                   4.2d0
        

      • mathematische, physikalischen oder programmspezifische Konstanten müssen - soweit irgend möglich -
        mit der erweiterten Genauigkeit angegeben werden z.B. statt
                real, parameter :: pi = 3.141593
        
        bei doppelter Genauigkeit
                double precision,  parameter :: pi = 3.14159265358979d0
                ! da genau 15 Stellen zugewiesen werden, ist hier d0 anzuhaengen nicht zwingend noetig
        

      • zum Programm gehörende Unterprogramme müssen in der Genauigkeit ebenfalls angepasst werden, z.B. aus
                real function (x) 
                   implicit none
                   real, intent(in) :: x
        
        wird bei doppelter Genauigkeit
                double precision function (x) 
                   implicit none
                   double precision, intent(in) :: x
        

      • Zusätzlich ist darauf zu achten, dass bei der Umwandlung ganzer Zahlen in reelle Zahlen statt
                 real( )
        
        nun
                 dble( )
        
        stehen muss.

      • Die Formatbeschreiber für die Ausgabe sind gegebenfalls anzupassen (Anzahl der Nachkommastellen!)

      • Unter Umständen muss (z.B. für die Anzahl an Schleifendurchläufen, wenn diese nun größer als 2^(31) werden können)
        der Darstellungsbereich des Datentyps integer z.B. durch selected_int_kind (siehe unten) erweitert werden.

    Statt d bzw. e als Kleinbuchstaben zu schreiben, kann man natürlich genausogut die Großbuchstaben verwenden, da Fortran zwischen Groß- und Kleinschreibung nicht unterscheidet.

    Da beim Datentyp double precision für die interne Zahlendarstellung mehr als die 4 Byte beim Datentyp real zur Verfügung stehen, wird auch der Wertebereich darstellbarer reeller Zahlen erweitert - und zwar von ca. +/- 38 als maximalem Wert des Exponenten auf doppelt genaue Zahlen ca. 14-15 Genauigkeitsstellen und maximal bis ca. +/-308 im Exponenten (d+308 bzw. d-308).

    Anwendungsbeispiel: Berechnung der Quadratwurzel aus 2 mit doppelter Genauigkeit (sqrt_2.f90).

    
    program sqrt_2
    implicit none
    double precision :: a, b, c, d
       a = sqrt(2.0d0)
       b = sqrt(2.0)
       c = sqrt(dble(2)) 
       d = sqrt(real(2))
       write(*,*)  'Berechnung von Wurzel aus 2 mit doppelter Genauigkeit'
       write(*,1)  'Ergebnis aus Maple: ','1.4142135623730950'
       write(*,5)  'richtig: sqrt(2.0d0)   = ', a
       write(*,10) 'falsch : sqrt(2.0)     = ', b, '<= !!! falsch !!!'
       write(*,5)  'richtig: sqrt(dble(2)) = ', c 
       write(*,10) 'falsch : sqrt(real(2)) = ', d, '<= !!! falsch !!!'
    1  format(1X,T2,A,T30,1X,A)   
    5  format(1X,T2,A,T30,F17.14)
    10 format(1X,T2,A,T30,F17.14,T49,A)
    end program sqrt_2
    

    Bildschirmausgabe beim Salford FTN95 (sqrt_2_ftn95.erg).

     Berechnung von Wurzel aus 2 mit doppelter Genauigkeit
     Ergebnis aus Maple:          1.4142135623730950
     richtig: sqrt(2.0d0)   =     1.41421356237310
     falsch : sqrt(2.0)     =     1.41421353816986  <= !!! falsch !!!
     richtig: sqrt(dble(2)) =     1.41421356237310
     falsch : sqrt(real(2)) =     1.41421353816986  <= !!! falsch !!!
    

    Bildschirmausgabe beim g95 (sqrt_2_g95.erg).

     Berechnung von Wurzel aus 2 mit doppelter Genauigkeit
     Ergebnis aus Maple:          1.4142135623730950
     richtig: sqrt(2.0d0)   =     1.41421356237309
     falsch : sqrt(2.0)     =     1.41421353816986  <= !!! falsch !!!
     richtig: sqrt(dble(2)) =     1.41421356237309
     falsch : sqrt(real(2)) =     1.41421353816986  <= !!! falsch !!!
    

    Wählt man eine unformatierte Ausgabe (sqrt_2u.f90), so gibt der Salford FTN95 weniger Stellen (genaugenommen 12) auf dem Bildschirm aus (sqrt_2u_ftn95.erg).

     Berechnung von Wurzel aus 2 mit doppelter Genauigkeit
     Ergebnis aus Maple:                1.4142135623730950
     richtig: sqrt(2.0d0)   =           1.41421356237
     falsch : sqrt(2.0)     =           1.41421353817    <= !!! falsch !!!
     richtig: sqrt(dble(2)) =           1.41421356237
     falsch : sqrt(real(2)) =           1.41421353817    <= !!! falsch !!!
    

    obwohl, wie wir oben gesehen haben, der Salford FTN95 Compiler intern beim Datentyp double precision auf ca. 14-15 Stellen genau arbeitet.

    Die äquivalente listengesteuerte Bildschirmausgabe des g95 ist (sqrt_2u_g95.erg).

     Berechnung von Wurzel aus 2 mit doppelter Genauigkeit
     Ergebnis aus Maple:       1.4142135623730950
     richtig: sqrt(2.0d0)   =  1.414213562373095
     falsch : sqrt(2.0)     =  1.4142135381698608 <= !!! falsch !!!
     richtig: sqrt(dble(2)) =  1.414213562373095
     falsch : sqrt(real(2)) =  1.4142135381698608 <= !!! falsch !!!
    

    Um besser vergleichen zu können, wurden in der obigen Darstellung wurde der Maple-Wert abweichend von der tatsächlichen Bildschirmausgabe noch um ein paar Leerstellen verschoben.

    Ein weiteres Beispiel zeigt, dass bei der Umstellung eines Programms auf doppelte Genauigkeit wirklich konsequent darauf geachtet werden muss, alle reellen Zahlenwerte innerhalb des Programmcodes im Exponenten von e bzw. E auf d bzw. D umzustellen bzw. an Zahlenwerte ohne Exponent d0 bzw. D0 anzuhängen (kugelvolumen_double.f90):

    
    program kugelvolumen
      implicit none
      double precision, parameter :: pi = 3.14159265358979d0
      double precision :: r, v1, v2, v3
      write(*,*) 'Berechnung des Volumens einer Kugel'
      write(*,'(1X,A$)') 'Bitte geben Sie den Radius ein: '
      read(*,*) r
      write(*,*) 'Eingelesen wurde: ', r
      v1 = 4.0d0/3.0d0 * pi * r**3
      v2 = 4.0d0/3.0 * pi * r**3
      v3 = 4.0/3.0 * pi * r**3
      write(*,*) 'Das Volumen der Kugel ist:'
      write(*,*) '4.0d0/3.0d0 *pi*r**3 =', v1, ' richtig'
      write(*,*) '4.0d0/3.0   *pi*r**3 =', v2, ' impl. Datentyp-Konversion'
      write(*,*) '4.0/3.0     *pi*r**3 =', v3, ' <= falsches Ergebnis !!!'
    end program kugelvolumen
    

    Das 2. Ergebnis wird nur wegen der impliziten Datentyp-Konversionsregel von Fortran richtig berechnet. In 4.0d0/3.0 wird ein Zahlenwert des Datentyps double precision durch einem arithmetischen Operator (/) mit einem Zahlenwert des Datentyps real verknüpft. Dabei wird die 2. Zahl in den Datentyp der höheren Genauigkeit umgewandelt (aus der Zahl des Datentyps real wird eine Zahl des Datentyps double precision), bevor die arithmetische Operation (die Division) durchgeführt und das Ergebnis (des Datentyps double precision) berechnet wird.

    Bildschirmausgabe (kugelvolumen_double.erg):

     Berechnung des Volumens einer Kugel
     Bitte geben Sie den Radius ein: 1.95
     Eingelesen wurde:           1.95000000000
     Das Volumen der Kugel ist:
     4.0d0/3.0d0 *pi*r**3 =          31.0593557697     richtig
     4.0d0/3.0   *pi*r**3 =          31.0593557697     impl. Datentyp-Konversion
     4./3.       *pi*r**3 =          31.0593566954     <= falsches Ergebnis !!!
    

    Das Beispiel zeigt auch, dassder Anwender seine Zahlenwerte als "ganz normale Zahlen" eingeben kann und nicht mit der Endung d0 zu versehen braucht, wenn das Einlesen der Zahlenwerte listengesteuert und damit ohne Formatangabe erfolgt. Im obigen Beispiel ist es z.B. ganz ein Ordnung, wenn ein Anwender 1.95 statt 1.95d0 als Zahleneingabe schreibt, weil das Programm beim listengesteuerten Einlesen mit der impliziten Datentyp-Konversion nach double precision sorgt.

    Manchmal ist es notwendig, auch komplexe Zahlen mit doppelter Genauigkeit zu definieren. Auch hier kann man die Abwärtskompatibilät von Fortran 90/95 zu FORTRAN 77 nutzen:

    "Doppelte" Genauigkeit (auf ca. 14-15 Stellen) für komplexe Zahlen:

    • Deklaration komplexer Zahlen mit doppelter Genauigkeit
            double complex ::  z1
      
      oder alternativ mit
       
            complex*16 :: z2 
    • Die Umwandlung reeller Zahlen doppelter Genauigkeit in komplexe Zahlen mit doppelter Genauigkeit erfolgt z.B. über
             double precision :: r1 = 2.0d0, i1 = 1.0d0
             ! Umwandlung in eine komplexe Zahl mit doppelter Genauigkeit
             z1 =  dcmplx(r1,i1) 
      

    Auch hier ist es wiederum wichtig, dass systematisch darauf geachtet wird, dass konsequent im gesamten Programm alle betroffenen Zahlenwerte und Rechenoperationen auf den Datentyp mit der doppelten Genauigkeit umgestellt werden müssen. Ansonsten kann es zu Rundungsfehlern aufgrund von unbeabsichtigten Verlusten in der Darstellungsgenauigkeit und damit zu numerischen Fehlern kommen.

    Das folgende Beispiel zeigt, wie wichtig es ist, bei der Umstellung auf double complex die Funktion cmplx( , ) durch dcmplx( , ) zu ersetzen und gleichzeitig darauf zu achten, dass an allen relevanten Stellen Werte des Datentyps real in Werte des Datentyp double precision konvertiert werden (komplex_double.f90):

    
    program komplex_double
      implicit none
      double complex :: i
      double complex :: z1,z11, z12, z2
      
      i   = dcmplx(0.0d0,1.0d0)
      z1  = dcmplx(0.4d0,-5.0d0/3.0d0)
      z11 = cmplx(0.4d0,-5.0d0/3.0d0)
      z12 = dcmplx(0.4,-5.0/3.0)
      z2  = i/z1
      
      write(*,*) 'z1  = dcmplx(0.4d0,-5.0d0/3.0d0)= ', z1
      write(*,*) 'z11 =  cmplx(0.4d0,-5.0d0/3.0d0)= ',z11 , '<= falsch!!!'
      write(*,*) 'z12 = dcmplx(0.4,  -5.0/3.0    )= ',z12 , '<= falsch!!!'
      write(*,*)
      write(*,*) 'z2 = i / z1 = ', z2
      write(*,*)
      write(*,*) ' abs(i)  = ', abs(i)
      write(*,*) ' abs(z2) = ', abs(z2)
      write(*,*) ' exp(i)  = ', exp(i)
      write(*,*) ' exp(z2) = ', exp(z2)
      
    end program komplex_double
    

    Bildschirmausgabe mit dem g95-Compiler (komplex_double_g95.erg):

     z1  = dcmplx(0.4d0,-5.0d0/3.0d0)=  (0.4,-1.6666666666666667)
     z11 =  cmplx(0.4d0,-5.0d0/3.0d0)=  (0.4000000059604645,-1.6666666269302368) <= falsch!!!
     z12 = dcmplx(0.4,  -5.0/3.0    )=  (0.4000000059604645,-1.6666666269302368) <= falsch!!!
    
     z2 = i / z1 =  (-0.56732223903177,0.1361573373676248)
    
      abs(i)  =  1.
      abs(z2) =  0.5834323811883104
      exp(i)  =  (0.5403023058681398,0.8414709848078965)
      exp(z2) =  (0.5617937820169099,0.07696856989800491)
    

    Analoge Ergebnisse erhält man mit dem Salford FTN95-Compiler (komplex_double_ftn95.erg). Ob die in Fortran eingebauten intrinsischen Funktionen wie z.B. abs( ) und exp( ) auch für doppelt genaue komplexe Zahlen ohne Genauigkeitsverlust arbeiten, lässt sich z.B. mit Maple gegenprüfen (komplex_double.html):

    Durch den Vergleich mit den Ergebnissen von Maple erkennt man, dass die intrisischen Fortran 90/95 - Funktionen (z.B. exp( )) ohne Genauigkeitsverlust mit double complex arbeiten. Es können die generischen Funktionsnamen weiterverwendet werden und der Compiler sorgt dafür, dass intern diejenigen Routinen aufgerufen werden, die dem übergebenen Datentyp entsprechen.

    Achtung: in FORTRAN 77 konnte es (je nach Compiler) unter Umständen zu Genauigkeitsverlusten kommen, falls z.B. nicht CDEXP( ) statt CEXP( ) und alle anderen intrinsischen Funktionsnamen auf die Datentypen mit doppelter Genauigkeit umgeschrieben wurden.

    Der compilerabhängige kind-Wert

    Bei der Deklaration von Variablen bzw. Konstanten lässt sich in Fortran 90/95 mit Hilfe von kind die Genauigkeit in der Zahlendarstellung umstellen.

    kind ist darüber hinaus eine in Fortran 90/95 enthaltene intrinsische Funktion, die einen (leider(!) compilerabhängigen) Zahlenwert für die einzelnen Werte zurückgeben kann.

    Test-Programm für die kind-Werte ( kind_demo.f90):

    program kind_demo
       implicit none
       write(*,*) 'kind(0.0)   =', kind(0.0)
       write(*,*) 'kind(0.0D0) =', kind(0.0D0)
    end program kind_demo
    

    Bildschirm-Ausgabe beim g95-Compiler ( kind_demo_g95.erg):

     kind(0.0)   = 4
     kind(0.0D0) = 8
    

    Der Salford FTN95 Compiler bringt beim gleichen Programm jedoch ( kind_demo_ftn95.erg):

     kind(0.0)   =           1
     kind(0.0D0) =           2
    

    Wie wir oben gesehen haben, ist 0.0 ein Wert vom Datentyp real mit einfacher Genauigkeit und 0.0D0 eine reelle Zahl mit doppelter Genauigkeit.

    Eine der Möglichkeiten, sich in Fortran 90/95 eine Zahl des höheren (doppelten) Genauigkeitstyps zu deklarieren, wäre z.B. mit dem g95 die folgende:

        real(kind=8) :: x, y  ! Achtung: compilerabhaengige Version
    
    welche an die Fortran 77 - Version
        REAL*8  X
    
    erinnert. Zahlwerte mit doppelter Genauigkeit ließen sich nun in Fortran 90/95 (compilerabhängig und nicht mehr universell portierbar) z.B. als
        x = 2.6_8      
        y = .7_8
    
    zuweisen. Problematisch ist jedoch bei diesem Verfahren, dass - wie wir von oben wissen - der kind-Wert für den reellen Datentyp mit doppelter Genauigkeit compilerabhängig ist. Würde man wie oben vorgehen, so müsste jeder doppelt genaue Zahlenwert im Programmcode für z.B. den g95 oder auch den Compaq Visual Fortran - Compiler mit _8 und z.B. für den Salford-Compiler mit _2 versehen werden. Zu diesem äussert unschönen Weg gibt es jedoch bessere Alternativen.

    Gleich zu Beginn des Deklarationsteils werden zwei integer-Konstanten deklariert, die die Genauigkeitsangabe beinhalten. Wird eine Genauigkeitsangabe benötigt, so wird mit den Konstantennamen gearbeitet:

       integer, parameter :: single = 4  ! muss compilerabhaengig angepasst werden
       integer, parameter :: double = 8  ! muss compilerabhaengig angepasst werden
       real(kind=single)  :: a
       real(kind=double)  :: x, y
    
    Den Variablen lassen sich dann innerhalb des Programms die Zahlwerte lassen z.B. als
        a = 100.2_single
        x = 2.6_double      
        y = .7_double
    
    zuweisen.

    Wird ein solches Programm von einem Compiler mit einem kind-Wert von 8 für die doppelte Genauigkeit (bzw. 4 für einfache Genauigkeit) arbeitenden Compiler auf einem Rechner mit einem Compiler, der als Zahlenwerte 2 (für doppelte) bzw. 1 (für einfache) Genauigkeit erwartet, transferiert, so muss in dem gesamten Programmcode nur noch zu Beginn die Zahlenwerte für double bzw. single angepasst werden.

    Die divergierende Entwicklung unterschiedlicher kind-Werte für reelle Zahlen einfacher und doppelter Genauigkeit bei den Compilerherstellern, wurde durch das für den Fortran 90/95 - Standard entwickelte selected_real_kind-Verfahren wieder aufgehoben.

    selected_real_kind = compiler-unabhängiges Verfahren zur Einstellung der Genauigkeit reeller Zahlen

    Mit selected_real_kind die Darstellungsgenauigkeit reeller Zahlen wählen

    Um den kind-Wert einer Zahl compilerunbhängig einzustellen zu können, wurde mit
          selected_real_kind(p=<Anzahl Genauigkeitsstellen>)
    
    oder
          selected_real_kind(r=<max.Exponentenwert zur Basis 10>)
    
    oder
          selected_real_kind(p=<Anzahl Genauigkeitsstellen>,r=<max.Exponentenwert zur Basis 10>)
    

    eine Methode geschaffen, die es erlaubt, die compilerabhängige kind-Zahl zu finden und auszuwählen, welche mindestens die vorgegebene Anzahl an Genauigkeitsstellen darstellen kann. Die Obergrenze setzt hier der verwendete Compiler. Zur Zeit gehen - mit wenigen rühmlichen Ausnahmen (ein Beispiel siehe unten) - die meisten Compiler nicht über die Genauigkeit von double precision hinaus.

    Mit p=<Anzahl Genauigkeitsstellen> lassen sich die benötigte Anzahl der Genauigkeitsstellen explizit angeben. Wenn man z.B. mindestens 8 Stellen Genauigkeit haben möchte, lässt sich dies formulieren als

          integer, parameter :: s8 = selected_real_kind(p=8) 
    
    oder als
          integer, parameter :: s8 = selected_real_kind(8) 
    
    Mit r=<max.Exponentenwert zur Basis 10> lässt sich der Zahlenbereich angeben, zu dem der Wertebereich dieser Zahlen definiert wird.

    Mit

          integer, parameter :: sp = selected_real_kind(p=13,r=200) 
    
    bzw.
          integer, parameter :: sp = selected_real_kind(13,200) 
    
    lässt sich der kind-Wert reeller Zahlen festlegen, die mit 13 Stellen Genauigkeit im Darstellungsbereich bis zum Betrag von 10^200 darstellbar sein sollen.

    Beispiel: hoehere_genauigkeit.f90

    program hoehere_genauigkeit
       implicit none
       ! eine Moeglichkeit, sich Variablen mit hoeherer Genauigkeit 
       ! als die 6-7 Stellen Genauigkeit des Datentyps real zu definieren
    
       ! Es wird ein real-Datentyp vereinbart, der mindestens 12 Stellen 
       ! Genauigkeit aufweist 
    
       integer, parameter :: s12 = selected_real_kind(p=12)  
    
    
       real           :: x1
       real(kind=s12) :: x2, x3
         
       x1 = 2.9/7.34
       x2 = 2.9_s12 / 7.34_s12     ! Achtung: Alle Zahlenwerte muessen
                                   ! explizit mit der neuen
                                   ! Genauigkeitsangabe
                                   ! versehen werden, sonst 
                                   ! wird weiterhin nur mit den
                                   ! 6-7 Stellen des Datentyps real
                                   ! gearbeitet
       x3 = 2.9/7.34
    
       write(*,*) 'x1 (Genauigkeit des Datentyps real)           =       ', x1
       write(*,*) 'x2 (12 Dezimalstellen Genauigkeit)            =  ', x2
       write(*,*) 'x3 (Genauigkeitsverlust durch falsche Angabe) =  ', x3
       write(*,*) '2.9/7.34 (Ergebnis von Maple)                 =           0.395095367847411'
       write(*,*)
       write(*,*) 'compilerabhaengig: selected_real_kind(p=12) = ', s12
       write(*,*) 
    
    end program hoehere_genauigkeit
    

    Beispiel: hoehere_genauigkeit_ftn95.erg (Salford FTN95)

     x1 (Genauigkeit des Datentyps real)           =           0.395095
     x2 (12 Dezimalstellen Genauigkeit)            =           0.395095367847
     x3 (Genauigkeitsverlust durch falsche Angabe) =           0.395095378160
     2.9/7.34 (Ergebnis von Maple)                 =           0.395095367847411
    
     compilerabhaengig: selected_real_kind(p=12) =            2
    

    Achtung: Wenn Sie die Genauigkeit in der Zahlendarstellung erhöhen wollen, müssen Sie konsequent alle (!) Zahlenwerte in dem gesamten Programm anpassen, sonst kann es passieren, dass die gewünschte Genauigkeit unbeabsichtigt auf 6-7 Stellen reduziert wird.

    Die interne Zahlendarstellung erfolgt anhand der vom Anwender gewählten Zahlenwerte für p bzw. r in selected_real_kind(p=, r= ) mit demjenigen kind-Wert der der einfachen oder der doppelten Genauigkeit entspricht. Insofern kommt man mit den meisten Compilern mit dieser Methode auch nicht über die Darstellungsgenauigkeit von ca. 14-15 Stellen des Datentyps double precision bzw. dem Darstellungsbereich von double precision von ca. -10^(308) bis 10^(308) für die größten bzw. von ca. -10^(-308) bis 10^(-308) für die kleinsten Zahlen hinaus.

    Eine Ausnahme bilden hier Compiler, die den Datentyp real intern mit noch mehr Byte (z.B. 16 Byte statt 4 Byte) codieren können (z.B. der Sun f95 - Compiler auf dem Computeserver des Rechenzentrums btrzxn.

    btrzxn> uname -a
    SunOS btrzxn 5.8 Generic_117350-28 sun4u sparc SUNW,Sun-Blade-1000
    
    Hier kann man z.B. ohne größeren Aufwand bis zu ca. 33 Stellen genau rechnen ( real_16byte.f95):

    program real_16byte
      implicit none
      integer, parameter :: p33 = selected_real_kind(p=33)
      real(kind=p33) :: a, b
    
      a = log(5.0_p33)
      b = log(5.0)
      
      write(*,*)           'log(5) (Ergebnis von Maple)  =  &
                           & 1.6094379124341003746007593332261876395' 
      write(*,'(1X,A,(ES41.33))') 'log(5.0_p33)                 = ', a
      write(*,'(1X,A,(ES41.33),A)') 'log(5.0)                     = ', b ,' <= !!!' 
      write(*,*)
      write(*,*) 'kind-Wert (compilerabhaengig) von 5.0_p33 : ', kind(5.0_p33)
    end program real_16byte
    

    Bildschirmausgabe( real_16_btrzxn.erg):

     log(5) (Ergebnis von Maple)  =   1.6094379124341003746007593332261876395
     log(5.0_p33)                 =   1.609437912434100374600759333226188E+00
     log(5.0)                     =   1.609437942504882812500000000000000E+00 <= !!!
     
     kind-Wert (compilerabhaengig) von 5.0_p33 :  16
    

    Dieses Beispiel zeigt zweierlei: mit dem entsprechenden Compiler lässt mit Hilfe von selected_real_kind(p= ) ein Fortran 90/95 - Programm leicht so umstellen, dass mit einer nochmals deutlich höheren Genauigkeit als double precision gerechnet werden kann. Und zum anderen zeigt sich auch hier, dass um Genauigkeitsverluste zu vermeiden, der Programmierer peinlichst genau darauf achten muss, wirklich alle(!) relevanten Stellen im Programmcode auf die Version mit höheren Genauigkeit umzustellen:

    Notwendige Umstellungen für selected_real_kind(p= <p_wert>)

    • alle reellen Zahlenwerte müssen mit der Genauigkeitsmarkierung versehen werden, z.B.
              integer, parameter :: p10=selected_real_kind(p=10)
              real(kind=p10)     :: ...        ! Variablenliste
      
      Z.B. statt
                 1.23e4 bzw. 4.2
      
      muss für für den Datentyp der höheren Genauigkeit geschrieben werden
                 1.23e4_p10  bzw. 4.2_p10
      

    • mathematische, physikalischen oder programmspezifische Konstanten müssen - soweit irgend möglich -
      mit der erweiterten Genauigkeit angegeben werden z.B. statt
              real, parameter :: pi = 3.141593
      
      bei dem konkreten Beispiel
              real(kind=p10), parameter :: pi = 3.141592654_p10
      

    • zum Programm gehörende Unterprogramme müssen in der Genauigkeit ebenfalls angepasst werden, z.B. aus
              real function (x) 
                 implicit none
                 real, intent(in) :: x
      
      wird
              real(kind=p10) function (x) 
                 implicit none
                 integer, parameter :: p10=selected_real_kind(p=10)
                 real(kind=p10), intent(in) :: x
      

    • Zusätzlich ist darauf zu achten, dass bei der Umwandlung ganzer Zahlen in reelle Zahlen statt
               real( )
      
      nun (wiederum nach der im Beispiel festgelegten Wert)
               real( ,kind=p10)
      
      stehen muss.

    • Die Formatbeschreiber für die Ausgabe sind gegebenfalls anzupassen (Anzahl der Nachkommastellen!)

    • Unter Umständen muss (z.B. für die Anzahl an Schleifendurchläufen, wenn diese nun größer als 2^(31) werden können)
      der Darstellungsbereich des Datentyps integer z.B. durch selected_int_kind (siehe unten) erweitert werden.

    Die Multi-Precision-Library

    Benötigt man reelle Zahlen mit den mehr als 14-15 Genauigkeitsstellen und steht einem kein Compiler zur Verfügung der in der Lage ist reelle Zahlen in 16 Byte zu codieren oder reicht dies immer noch nicht aus, so kann man z.B. noch auf die Multi-Precision-Library zurückgreifen. Siehe hierzu z.B. das Projekt von Volkmar Klatt.

    Erweiterung des Darstellungsbereichs beim Datentyp integer

    Der Wertebereich aus dem Daten vom Datentyp integer bei den üblichen 4-Byte-Binärdarstellung umfasste bisher -2**31+1 bis 2**(31). Für den Datentyp integer bietet dementsprechend selected_int_kind(r=<Exponentenwert>) eine Möglichkeit, den Darstellungsbereich der ganzen Zahlen zu erweitern. Der Zahlenwert Exponentenwert in der Klammer gibt die Anzahl der Stellen an. Dies entspricht einem Wertebereich ganzer Zahlen im Bereich von -10**(Exponentenwert) bis 10**(Exponentenwert).

    Benutzerdefinierte Datentypen (Die type-Vereinbarungsanweisung)

    Die in Fortran enthalten Datentypen lassen sich zu benutzerdefinierten Datentypen verknüpfen mittels

    type :: <Name des zusammengesetzten Datentyps > 
            sequence      ! optional: sorgt dafuer, dass die einzelnen 
                          !Bestandteile zusammenhaengend im Speicher abgelegt werden
            ! Deklaration der einzelnen Komponenten 
               ... 
    end type <Name des zusammengesetzten Datentyps > 
    

    Eine Variable des selbstkonstruierten Datentyp lässt sich deklarieren durch

    type(<Name des zusammengesetzten Datentyps >) :: <Variablennamen>
    

    Zum Beispiel lässt sich leicht ein benutzerdefinierter Datentyp student erzeugen, der sich aus dem Vornamen, dem Nachnamen und der Matrikel-Nummer der Studentin oder des Studenten zusammensetzt. Mit dem durch eine type-Vereinbarungsanweisung deklarierten Datentyp kann man genauso, wie bisher geschehen, durch die Angabe von dimenension( ) ein Datenfeld (Array) mit diesen Komponenten erzeugen (siehe Programm-Beispiel).

    Innerhalb eines Programmes kann auf eine im type-Verbund enthaltene Variable durch Angabe des Variablennamens gefolgt von einem Prozent-Zeichen und dem Namen der Unterkomponente zugegriffen werden.

    Referenzierung auf eine Komponente in einem type-Verbund

         <Name der type-Variable>%<Name einer Verbundkomponente>
    

    Durch den Zugriff auf eine Unterkomponente des type-Verbunds kann deren Wert bei Bedarf verändert bzw. ausgeben werden. Soll eine

    Wertzuweisungen an die Gesamtheit aller Komponenten

    eines zusammengesetzten Datentyps erfolgen (z.B. als Initialisierung), so kann dies z.B. über

         <Name der type-Variable>=<Name des zusammengesetzten Datentyps>(Liste der Werte)
    

    geschehen.

    Beispiel: type_einfach.f90

    module datenbankdefinition
       implicit none
       save 
       integer, parameter           :: n = 3  ! Anzahl Datensaetze 
       character(len=35), parameter :: str = '(2X,I2,3X,A25,1X,A25,1X,A6/)'
                                              ! fuer formatierte Ausgabe von student
       type :: student
         character(len=25) :: vorname, nachname
         character(len=6)  :: matrikel_nr 
       end type student
    
       type(student), dimension(n)   :: personen
    end module datenbankdefinition
    
    
    
    program type_einfach
       use datenbankdefinition 
       implicit none 
    
       character(len=6)    :: nummer
       integer             :: i
    
    !---------------------
    !  Formatbeschreiber
    !---------------------
    
    15 format(1X,A$)     ! Ausgabeformat: Zeichenkonstante mit Unterdrueckung 
                         ! des Zeilenvorschubs
    20 format(A6)        ! Einleseformat Matrikelnummer
    25 format(A25)       ! Einleseformat Matrikelnummer
    
    !-------------------------------------------------------------   
    ! Wertzuweisung fuer den zusammengesetzten Datentyp
    !-------------------------------------------------------------   
    
       personen(1) =  student('Hans','Maier','123456')
       personen(2) =  student('Irene','Huber','111111')
       personen(3) =  student('Maria', 'Rose','000001')
    
       call datenbank_ausgabe
    
    !-------------------------------------------------------------
    ! Den Nachnamen einer Person aendern
    !-------------------------------------------------------------
    
       write(*,*) 
       write(*,*) '====================================='
       write(*,*)      'Funktion: Nachname aendern:'
       write(*,*) '====================================='
       write(*,15) 'Bitte geben Sie die Matikelnummer ein: '
       read(*,20) nummer
    
       do i = 1, n 
          if (nummer == personen(i)%matrikel_nr) then
              write(*,*) 
              write(*,str) i, personen(i)%vorname, personen(i)%nachname, & 
                    personen(i)%matrikel_nr
              write(*,15) '      >>>  neuer Nachname  =>   '; 
              read(*,25)   personen(i)%nachname
           end if
       end do
    
       call datenbank_ausgabe
    end program type_einfach
    
    
    subroutine datenbank_ausgabe
       use datenbankdefinition 
       implicit none
       integer :: i
       write(*,*)
       write(*,*) '====================================='
       write(*,*) 'Funktion: Ausgabe der Datenbank:'
       write(*,*) '====================================='
             do i = 1, n 
                   write(*,str) i, personen(i)%vorname, personen(i)%nachname, & 
                   personen(i)%matrikel_nr
             end do
       return
    end subroutine datenbank_ausgabe
    

    Bildschirmausgabe im obigen Beispiel: type_einfach.erg

     
     =====================================
     Funktion: Ausgabe der Datenbank:
     =====================================
       1   Hans                      Maier                     123456
    
       2   Irene                     Huber                     111111
    
       3   Maria                     Rose                      000001
    
     
     =====================================
     Funktion: Nachname aendern:
     =====================================
     Bitte geben Sie die Matikelnummer ein: 123456
     
       1   Hans                      Maier                     123456
    
           >>>  neuer Nachname  =>   Lauterbach
     
     =====================================
     Funktion: Ausgabe der Datenbank:
     =====================================
       1   Hans                      Lauterbach                123456
    
       2   Irene                     Huber                     111111
    
       3   Maria                     Rose                      000001
    
    

    Ein weiteres Beispiel: gewichtsberechnung.f90

    ! --------------------------------------------
    ! Das Programm berechnet aus 
    !         Radius und Laenge sowie
    !         der Dichte 
    ! die Masse von massiven Zylinderkoerpern   
    ! --------------------------------------------
    
    
    module koerperdefinition 
        implicit none
        save
       
        real, parameter :: pi = 3.141593
        character(len=74), parameter :: fb_zylinder = & 
         "(/3X,'Radius =',1X,G12.5/,3X,'Laenge =',1X,G12.5/,3X,'Dichte =',1X,G12.5)"
        
        type :: zylinder
                sequence 
                real :: radius
                real :: laenge
                real :: dichte
         end type zylinder
    end module koerperdefinition
    
    
    module koerpermethoden
        use koerperdefinition
        implicit none 
        save
    
        contains
    
        subroutine ausgabe(zyli)
        ! formatierte Ausgabe der Zylinderdaten
           implicit none
           type(zylinder), intent(in) :: zyli
           write(*,fb_zylinder) zyli%radius, zyli%laenge, zyli%dichte
        end subroutine ausgabe
     
        subroutine einlesen(zyli)
        ! formatierte Ausgabe der Zylinderdaten
           implicit none
           type(zylinder), intent(out) :: zyli
           real :: r, l, d
           5 format(1X,A$)
           write(*,*) 
           do
              write(*,5) ' Radius = '; read(*,*) r
              if ( r > 0.0 ) exit
           end do
           do
              write(*,5) ' Laenge = '; read(*,*) l
              if ( l > 0.0 ) exit
           end do
           do
              write(*,5) ' Dichte = '; read(*,*) d
              if ( d > 0.0 ) exit
           end do
           write(*,*) 
           zyli = zylinder(r,l,d)
        end subroutine einlesen
     
        real function volumen(zyli)
        ! berechnet das Volumen eines Zylinders
           implicit none
           type(zylinder),intent(in) :: zyli 
              volumen = pi * (zyli%radius)**2 * (zyli%laenge)           
        return
        end function volumen
    
        real function masse(zyli)
        ! berechnet die Masse des Zylinders 
            implicit none
            type(zylinder),intent(in) :: zyli 
               masse = pi * (zyli%radius)**2 * (zyli%laenge) * (zyli%dichte) 
         return
         end function masse    
    
    end module koerpermethoden
    
    
    program gewichtsberechnung
      use koerpermethoden
      implicit none
      type(zylinder) :: koerper1 = zylinder(2.0,5.0,19.3)
      type(zylinder) :: koerper2 
       
      write(*,*) 'Der 1. Zylinder wird beschrieben durch:'
      call ausgabe(koerper1)
      write(*,*)
      write(*,*) 'Berechnete Groessen:'
      write(*,*) '     Volumen = ', volumen(koerper1)
      write(*,*) '     Masse   = ', masse(koerper1)
      write(*,*)
      write(*,*)  
      write(*,*) 'Fuer den 2. Zylinder werden nun von Ihnen die Werte eingelesen'
      call einlesen(koerper2)
      write(*,*) ' Die von Ihnen eingegebenen Werte '
      call ausgabe(koerper2)
      write(*,*)
      write(*,*) 'Berechnete Groessen:'
      write(*,*) '     Volumen = ', volumen(koerper2)
      write(*,*) '     Masse   = ', masse(koerper2)
      write(*,*)
       
    end program gewichtsberechnung
    

    Bildschirmausgabe: gewichtsberechnung.erg

     Der 1. Zylinder wird beschrieben durch:
    
       Radius =   2.0000    
       Laenge =   5.0000    
       Dichte =   19.300    
     
     Berechnete Groessen:
          Volumen =    62.83186    
          Masse   =    1212.655    
     
     
     Fuer den 2. Zylinder werden nun von Ihnen die Werte eingelesen
     
      Radius = 1.0
      Laenge = 1.0
      Dichte = 1.0
     
      Die von Ihnen eingegebenen Werte 
    
       Radius =   1.0000    
       Laenge =   1.0000    
       Dichte =   1.0000    
     
     Berechnete Groessen:
          Volumen =    3.141593    
          Masse   =    3.141593    
    

    Das letzte Beispiel zeigt die Definition eines benutzerdefinierten Datentyps zylinder in einer type-Vereinbarung.

    Das module koerperdefinition wird zum einen zur type-Vereinbarung des Datentyps genutzt, zum anderen werden gleichzeitig eng mit diesem Datentyp verbundene Konstantenwerte festgelegt. Neben dem Wert von pi wird ein Ausgabeformatbeschreiber definiert, der später zur Ausgabe des verknüpften Datentyps eingesetzt wird.

    In einem zweiten Module koerpermethoden werden sämtliche Unterprogramme definiert, die im Umgang mit dem Datentyp zylinder gebraucht werden. Dies sind hier zum einen eine Routine zur Ausgabe, eine zum Einlesen und zwei Berechnungsroutinen.

    Der Ansatz sich jeweils Module zur Datentyp-Definition und für die Methoden im Umgang mit dem Datentyp) hat als Vorteile Wiederverwendbarkeit, klare Struktur und die Vereinfachung des Umgangs mit abgeleiteten Datentypen.

    Gewissermaßen handelt es sich um einen objektorientierten Ansatz. Es werden Objekte und Methoden im Umgang mit diesen Objekten definiert. Die Objekte und Methoden sind "gekapselt" und ein anderer Programmierer, der diese Module nicht selbst entwickelt hat, kann mit den Objekten "arbeiten", wenn er nur genaue Kenntnisse über die Schnittstellen und das Verhalten der Routinen hat. Detailkenntisse über den internen Aufbau würden in diesem Fall nicht benötigt (Stichwort: modulare Softwareentwicklung, modularer Softwareaufbau).

Zurück zur Vorlesungsseite


Heidrun.Kolinsky@uni-bayreuth.de
(Dr. Heidrun Kolinsky, Rechenzentrum der Universität Bayreuth, Gebäude NW2, Raum 159, Universitätsstraße 30, D-95440 Bayreuth, Tel. 0921/55-2687)