COMPLEX :: A, B, CVid in- och utmatning betraktas komplexa tal som två reella tal, varför eventuell format specifikation måste ges för två tal. Vid liststyrd inmatning skall de båda delarna ges med parentes omkring samt med komma och/eller blank emellan.
Ett enkelt exempel på ett program med komplexa tal följer här.
PROGRAM KOMPLEX REAL :: A, B, D COMPLEX :: C, C1, C2 A = 3.0 B = 5.0 C1 = CMPLX(A,B) ! Bilda ett komplext tal C2 = (4.0, 8.0) ! Bilda ett komplext tal WRITE(*,*) ' C1 = ', C1 WRITE(*,10) C2 10 FORMAT(' C2 = ',F10.3,E12.3) A = REAL(C1) ! Ta realdelen B = AIMAG(C1) ! Ta imaginärdelen C = CONJG(C1) ! Komplexkonjugera D = ABS(C1) ! Ta beloppet WRITE(*,20) A, B, C, D 20 FORMAT(' Realdelen av C1 = ', F10.3,/, & ' Imaginärdelen av C1 = ', F12.3,/, & ' Konjugerade C1 = ', 2F10.3,/, & ' Absolutbeloppet av C1 = ', F10.3) WRITE(*,*) 'Ge nya värden på C1 i list-styrt format' READ(*,*) C1 WRITE(*,*) 'Ge nya värden på C2 i formaterad form' READ(*,'(2F10.3)') C2 WRITE(*,*) ' C1 = ', C1 WRITE(*,10) C2 END PROGRAM KOMPLEXKörning av ovanstående program komplex.f90 på en Sun följer. Notera att vid list-styrd inmatning måste jag ge det komplexa talet inom parenteser, och vid formaterad inmatning måste jag placera de båda delarna i rätt kolumner (dvs 1-10 för realdelen och 11-20 för imaginärdelen), eftersom jag använt formatet 2F10.3.
f90 komplex.f90 a.out C1 = ( 3.0000000, 5.0000000) C2 = 4.000 0.800E+01 Realdelen av C1 = 3.000 Imaginärdelen av C1 = 5.000 Konjugerade C1 = 3.000 -5.000 Absolutbeloppet av C1 = 5.831 Ge nya värden på C1 i list-styrt format (12.0 , 25.0) Inmatade värden Ge nya värden på C2 i formaterad form 7.6 8.3 Inmatade värden C1 = ( 12.0000000, 25.0000000) C2 = 7.600 0.830E+01
Vid flera andra programspråk kan man välja om man vill arbeta i enkel eller dubbel precision för hela programmet, för Fortran gäller att man har full frihet att välja vilka satser som skall utföras i enkel precision och vilka som skall utföras i dubbel precision. Man måste härvid deklarera vilka variabler som skall lagras i dubbel precision med deklarationen
DOUBLE PRECISION :: listaVid användning av dubbel precision är det mycket viktigt att komma ihåg att låta alla aktuella variabler vara i dubbel precision, annars förlorar man lätt noggrannhet, till exempel om man räknar ut skillnaden D - E, där D är en variabel i dubbel precision och E är en variabel i enkel precision, så blir resultatet av följande steg
DOUBLE PRECISION :: D REAL :: E ... E = 0.1 D = D - Eatt eftersom E lagrar en tiondel binärt i enkel precision, dvs med ett avrundningsfel svarande mot enkel precision, att även D får ett avrundningsfel svarande mot enkel precision. Det rätta sättet att skriva är i detta fall, om man vill minska med en tiondel,
DOUBLE PRECISION :: D, E ... E = 0.1D0 D = D - Eeller enklare
DOUBLE PRECISION :: D ... D = D - 0.1D0I den första alternativlösningen är det inte absolut nödvändigt att ha med D0 i 0.1D0 för att instruera systemet att tiondelen skall beräknas i dubbel precision, eftersom en intelligent kompilator känner av detta, men det är säkrast. I den andra alternativlösningen är det mer nödvändigt eftersom det där är ett sammansatt uttryck. För sådana gäller att uttrycken automatiskt omvandlas till dubbel precision i enlighet med vissa specifika men krångliga regler, varför det finns risk att konverteringen till dubbel precision sker efter uträkningen av tiondelen i stället för vid uträkningen.
Ett enkelt exempel på ett meningslöst program utnyttjande dubbel precision följer.
PROGRAM DUBBEL DOUBLE PRECISION :: D, E, F REAL :: X, Y, Z INTEGER :: K ... Y = 5.2 - 0.7*F ! Enkel precision E = 5.2 - 0.7*F ! Enkel precision D = 5.2D0 - 0.7D0*F ! Dubbel precision (säker) X = D*F ! Minskad precision F = K - 20 ! Heltal till dubbel precision E = D + DBLE(K-20) ! Dubbel precision (säker) IF ( F < 10.0D0 ) THEN F = F + 0.3D0 END IF ... END PROGRAM DUBBEL
Raden 5.2 - 0.7*F är faktiskt intressant. De båda enkelprecisionstalen 5.2 och 0.7 utvidgas till dubbel precision på grund av faktorn F i dubbel precision (men värdet av konstanterna blir eventuellt motsvarande enkel precision) och beräkningen av uttrycket sker i dubbel precision, varefter resultatet lagras i enkelprecisionsvariabeln Y respektive dubbelprecisionsvariabeln E.
Det finns således en viss frihet för kompilatorskrivaren att låta kompilatorn förbättra det program som användaren skrivet, genom att konvertera konstanter av typ decimaltal till dubbel precision i blandade uttryck. Om explicit mellanlagring, i en variabel deklarerad som enkel precision, skall ske är det dock förbjudet att efteråt använda en högre noggrannhet på motsvarande storhet.
Vid konvertering av ett program från enkel till dubbel precision kan det räcka med att glömma konvertera en enda variabel för att nästan hela förbättringen i noggrannhet skall gå förlorad.
I Fortran 90 finns dels standardfunktioner för att undersöka vilken precision som gäller (se Appendix 5, avsnitt 8, där exempelvis PRECISION(X) ger antalet signifikanta siffror hos tal av samma slag som variabeln X), och dels möjligheten att vid deklarationen av en variabel ange hur många signifikanta siffror som minst skall rymmas i flyttal av denna typ (eller detta slag, jämför nedan, avsnitt 10.4). De båda vanliga precisionerna enkel precision (SP) och dubbel precision (DP) på ett IEEE 754 baserat system deklareras med
INTEGER, PARAMETER :: SP = SELECTED_REAL_KIND(6,37) INTEGER, PARAMETER :: DP = SELECTED_REAL_KIND(15,307) REAL(KIND=SP) :: enkelprecisionsvariabler REAL(KIND=DP) :: dubbelprecisionsvariablerOm man vill arbeta med exempelvis minst 14 decimala siffrors noggrannhet och minst en decimal exponent om + 300 så väljer man
INTEGER, PARAMETER :: AP = SELECTED_REAL_KIND(14,300) REAL(KIND=AP) :: arbetsprecisionsvariablerTyvärr måste man nu ge alla flyttalskonstanter med tillägget _AP, till exempel
REAL(KIND=AP) :: PI PI = 3.141592653589793_APmedan för de inbyggda funktionerna gäller att dessa är generiska, det vill säga de känner automatiskt av vilken datatyp (slag) som argument har och väljer då själva vilket slag som resultatet skall ha (oftast samma som argumentet).
Med denna metod får man i praktiken dubbel precision på datorer baserade på IEEE 754, men enkel precision på datorer som Cray eller datorer baserade på Digital Equipments Alpha-processor, dvs i samtliga fall en verklig precision om ungefär 15 signifikanta siffror.
Den icke-standardiserade datatypen DOUBLE PRECISION COMPLEX eller COMPLEX*16 erhålles helt enkelt genom att i stället skriva deklarationen som till exempel COMPLEX(KIND=2) eller COMPLEX(KIND=16). Detta kan ske på de system som har detta slag definierat på ett lämpligt sätt. Se till exempel Appendix 6 eller 7, varav framgår att detta gäller en del vanliga system. I de fall man inte känner till slags-parameterns aktuella värden ger man i stället följande deklarationer och tilldelningar.
INTEGER, PARAMETER :: AP = SELECTED_REAL_KIND(14,300) COMPLEX(KIND=AP) :: AA ... AA = (0.1_AP, 0.2_AP)
KIND(0) Heltal KIND(0.0) Flyttal KIND(.FALSE.) Logisk variabel KIND("A") TextsträngDet finns en inbyggd funktion SELECTED_REAL_KIND som ger det slag av typen REAL vars representation har (åtminstone) en viss precision och ett visst exponentutrymme. Till exempel så ger funktionen SELECTED_REAL_KIND(8,70) det slag av REAL som har åtminstone 8 decimala siffrors noggrannhet och som tillåter belopp mellan 10-70 och 10+70.
För heltal heter den motsvarande funktionen SELECTED_INT_KIND och har naturligtvis bara ett argument. Med till exempel ett val SELECTED_INT_KIND(5) tillåtes alla heltal mellan -99 999 och +99 999. Slaget hos en typ kan tilldelas ett namn.
INTEGER, PARAMETER :: K5 = SELECTED_INT_KIND(5)Detta slag av heltal användes sedan i konstanter enligt
-12345_K5 +1_K5 2_K5dvs på ett ganska onaturligt sätt, efter värdet följer ett understrykningstecken samt slagets namn. Däremot kan variabler av det nya heltals-slaget deklareras på ett trevligare sätt
INTEGER (KIND=K5) :: IVARMotsvarande gäller för flyttalsvariabler, om vi först inför en hög precision LONG med
INTEGER, PARAMETER :: LONG = SELECTED_REAL_KIND(15,99)så får vi ett flyttals-slag med minst femton decimala siffrors noggrannhet och med minst exponent-området från 10-99 till 10+99. Motsvarande konstanter erhålles med
2.6_LONG 12.34567890123456E30_LONGoch variablerna deklareras med
REAL (KIND=LONG) :: LASSEDe gamla typomvandlingarna INT, REAL och CMPLX har utvidgats. Med funktionen
INT(X, KIND = K5)omvandlas ett flyttal X till ett heltal av slaget K5, medan om Z är ett komplext flyttal så ger
REAL(Z, KIND(Z))omvandling till ett reellt flyttal av samma slag som Z (dvs realdelen av Z).
Dubbel precision finns inte inbyggd i "nya" Fortran 90 på något annat sätt än i "gamla" Fortran 77, men det förutsättes att kompilatorn innehåller stöd för att utnyttja i hårdvaran eventuellt inbyggd dubbel eller fyrdubbel precision, så att man själv enkelt definierar ett lämpligt slag av REAL som DP eller QP. Man kan naturligtvis även använda det gamla begreppet DOUBLE PRECISION.
Anledningen till att man krånglat till det hela på detta sättet är dels att man inte vill ha alltför många obligatoriska precisioner (enkel, dubbel, fyrdubbel; eventuellt för de båda fallen REAL och COMPLEX), dels att det gamla begreppet DOUBLE PRECISION inte innebar någon viss specificerad maskinnoggrannhet. Nu kan man "enkelt" specificera både precisionen och exponentområdet. Ytterligare information om slag ges i Appendix 6, där de olika datatypernas normala slag på både DEC (DEC Station Ultrix) och Sun liksom IBM PC ges vid NAG:s system för Fortran 90. Motsvarande för Cray finns i Appendix 7.
(10.2) Deklarera några variabler av ovanstående flyttalstyp.
Lösning.
(10.3) Deklarera några konstanter av ovanstående flyttalstyp.
Lösning.
TYPE stab_medlem CHARACTER(LEN=20) :: foernamn, efternamn INTEGER :: id, avdelning END TYPEvilket kan utnyttjas för att beskriva en individ. En kombination av individer kan likaså bildas.
TYPE(stab_medlem), DIMENSION(100) :: stabIndivider kan refereras som stab(nummer) och ett visst fält kan refereras som stab(nummer)%foernamn. Man kan även kapsla definitioner
TYPE bolag CHARACTER(LEN=20) :: bolagsnamn TYPE(stab_medlem), DIMENSION(100) :: stab END TYPE ... TYPE(bolag), DIMENSION(10) :: flera_bolagEtt numeriskt mer intressant exempel är en gles matris A med högst hundra element skilda från noll, som kan deklareras med hjälp av
TYPE NONZERO REAL VALUE INTEGER ROW, COLUMN END TYPEoch
TYPE (NONZERO) :: A(100)Man får då värdet av A(10) genom att skriva A(10)%VALUE. Tilldelning kan ske med exempelvis A(15) = NONZERO(17.0,3,7).
För att kunna använda användardefinierade datatyper i COMMON eller EQUIVALENCE, eller för att se till att två likadana datatyper verkligen betraktas som samma datatyp användes kommandot SEQUENCE, i det senare fallet får dessutom ingen variabel vara deklarerad PRIVATE. Anledningen till dessa extraregler är att normalt är inte ordningen för komponenterna i en struktur fastställd. Kommandot SEQUENCE medför att komponenterna lagras i samma ordning som de uppräknas. Det skall alltid stå direkt efter TYPE kommandot. Följande exempel är hämtat från Überhuber och Meditz (1993) och något modifierat. De påpekar att SEQUENCE inte är något egentligt attribut, eftersom det måste avse alla komponenterna.
TYPE BOK SEQUENCE CHARACTER (LEN = 50) :: FOERFATTARE, TITEL, FOERLAG INTEGER :: UTGIVNINGSAAR, ISBN_NUMMER REAL :: PRIS END TYPE BOK