A2 OOOOOO DO EIIR BettanFöljande variabelnamn är ej tillåtna under Fortran 77 (för långa och/eller innehåller understrykningstecken) men väl under Fortran 90:
GUSTAVUS ADOLFUS GUSTAV_ADOLF HEJ_DU_GLADE GOETEBORG ABCDEFGHIJKLMNOPQRSTUVWXYZFöljande variabelnamn är inte tillåtna under Fortran 77 och inte heller under Fortran 90:
2C inleds av siffra 2_CESAR inleds av siffra ÅKE ej tillåtet med Å $KE ej tillåtet med $ C-B ej tillåtet med minustecken (men korrekt som uttryck) DOLLAR$ ej tillåtet med $ K**2 ej tillåtet med * (men korrekt som uttryck) _STOCKHOLM_ ej tillåtet med inledande _ (understrykningstecken)(3.2) Denna uppgift återkommer som övning (22.13).
(3.3) Denna uppgift återkommer som övning (22.12).
(3.4) En rekursiv variant av denna funktion finns i avsnittet 5.2.1.3. Här följer i stället en konventionell beräkning av fakulteten utnyttjande en DO-slinga.
PROGRAM HELTALS_FAKULTET IMPLICIT NONE INTEGER :: N INTEGER, EXTERNAL :: FAK WRITE(*,*) ' Heltalsberäkning av fakulteten' DO WRITE(*,*) ' Ge önskat värde på argumentet' READ(*,*) N IF (N < 0 ) EXIT WRITE(*,*) ' För N = ', N, & ' blir fakulteten = ', FAK(N) END DO END PROGRAM HELTALS_FAKULTET INTEGER FUNCTION FAK(N) IMPLICIT NONE INTEGER :: N, I FAK = 1 IF (N < 0 ) THEN WRITE(*,*) ' FELAKTIGT VÄRDE PÅ ARGUMENTET' STOP ELSE IF (N > 1 ) THEN ! Både N = 0 och 1 ger här fakulteten 1 DO I = 2, N FAK = I*FAK END DO END IF END FUNCTION FAKPå en "normal" dator med 32 bitars heltal blir 10! korrekt uträknat till 3 628 800, men 13! blir felaktigt 1 932 053 504. Att detta är fel inses av att 13! måste ha minst lika många nollor på slutet som 10! Man får dock inte rätt värde genom att lägga till två nollor, siffrorna stämmer inte. Nästa värde 14! blir uträknat mindre än det felaktiga på 13!, och 17! blir negativ. Dessa problem beror på att heltals-spill (eng. integer overflow) normalt ej signaleras, utan beräkningen bara fortsätter.
PROGRAM FLYTTALS_FAKULTET IMPLICIT NONE REAL :: N REAL, EXTERNAL :: FAK WRITE(*,*) ' Flyttalsberäkning av fakulteten' DO WRITE(*,*) ' Ge önskat värde på argumentet' READ(*,*) N IF (N < 0.0 ) EXIT WRITE(*,*) ' För N = ', N, & ' blir fakulteten = ', FAK(N) END DO END PROGRAM FLYTTALS_FAKULTET REAL FUNCTION FAK(N) IMPLICIT NONE REAL :: N INTEGER :: I FAK = 1.0 IF (N < 0.0 ) THEN WRITE(*,*) ' FELAKTIGT VÄRDE PÅ ARGUMENTET' STOP ELSE IF (N > 1.0 ) THEN ! Både N = 0 och 1 ger här fakulteten 1 DO I = 2, NINT(N) ! Avrundning FAK = REAL(I)*FAK END DO END IF END FUNCTION FAKPå en "normal" dator med IEEE aritmetik och enkel precision blir 10! korrekt uträknat till 3,628800·10+6, medan 13! blir 6,2270208·10+9. Man får vidare 34! approximativt lika med 2,9523282·10+38, medan 35! ger spill. Detta beror på att flyttals-spill (eng. real overflow) normalt signaleras, och att beräkningen avbrytes då. Flyttal är därför säkrare att arbeta med än heltal i fall som detta.
PROGRAM SINUS ! TABELLERING AV SINUS-FUNKTIONEN IMPLICIT NONE DOUBLE PRECISION :: A, B, H, X INTEGER :: N, I INTRINSIC SIN 1 WRITE(*,*) ' Ge önskat intervall och antal delintervall!' READ(*,*) A, B, N IF ( A > B .OR. N <= 0 ) GO TO 1 H = (B - A)/DBLE(N) WRITE(*,*) WRITE(*,*) ' Tabellering av sinus-funktionen sin(x) ' WRITE(*,*) WRITE(*,*) ' x sin(x)' WRITE(*,*) DO I = 0, N X = A + DBLE(I)*H WRITE(*,20) X, SIN(X) END DO 20 FORMAT(1X,F12.6,F26.16) WRITE(*,*) STOP ENDSom synes valde jag att göra denna beräkning i dubbel precision. Körning med enkla indata gav följande resultat.
Ge önskat intervall och antal delintervall! 0 1 10 Tabellering av sinus-funktionen sin(x) x sin(x) 0.000000 0.0000000000000000 0.100000 0.0998334166468282 0.200000 0.1986693307950612 0.300000 0.2955202066613396 0.400000 0.3894183423086505 0.500000 0.4794255386042030 0.600000 0.5646424733950354 0.700000 0.6442176872376911 0.800000 0.7173560908995228 0.900000 0.7833269096274834 1.000000 0.8414709848078965(3.7)
PROGRAM ETTAN ! Kontroll av de trigonometriska funktionerna SIN och COS IMPLICIT NONE REAL :: A, B, H, X REAL :: Y, Z, ETTA, MAXFEL INTEGER :: N, I INTRINSIC SIN, COS 1 WRITE(*,*) ' Ge önskat intervall och antal delintervall!' READ(*,*) A, B, N IF ( A > B .OR. N <= 0 ) GO TO 1 H = (B - A)/REAL(N) WRITE(*,*) WRITE(*,*) ' Beräkning av felet vid beräkning av ', & ' sinus och cosinus' WRITE(*,*) MAXFEL = 0.0 DO I = 0, N X = A + REAL(I)*H Y = SIN(X) Z = COS(X) ETTA = Y**2 + Z**2 MAXFEL = MAX(MAXFEL, ABS(ETTA-1.0)) END DO WRITE(*,20) MAXFEL 20 FORMAT(1X,E14.6) WRITE(*,*) END PROGRAM ETTANResultatet av beräkningen på Sun blev föga överraskande 2·my = 2· = 0,1192·10-6.
(3.8) Denna uppgift återkommer som laboration 4 b. Någon lösning ges därför ej här.
(4.1) Detta är en mycket avancerad variant till lösning!
SUBROUTINE LOES_LIN_EKV_SYSTEM(A, X, B, FEL) IMPLICIT NONE ! Fältdeklarationer REAL, DIMENSION (:, :), INTENT (IN) :: A REAL, DIMENSION (:), INTENT (OUT) :: X REAL, DIMENSION (:), INTENT (IN) :: B LOGICAL, INTENT (OUT) :: FEL ! Arbetsarean M är A utvidgad med B REAL, DIMENSION (SIZE (B), SIZE (B) + 1) :: M INTEGER, DIMENSION (1) :: MAX_LOC REAL, DIMENSION (SIZE (B) + 1) :: TEMP_ROW INTEGER :: N, K, I ! Initiera M N = SIZE (B) M (1:N, 1:N) = A M (1:N, N+1) = B ! Triangulariseringsfas FEL = .FALSE. TRIANG_SLINGA: DO K = 1, N - 1 ! Pivotering MAX_LOC = MAXLOC (ABS (M (K:N, K))) IF ( MAX_LOC(1) /= 1 ) THEN TEMP_ROW (K:N+1 ) = M (K, K:N+1) M (K, K:N+1)= M (K-1+MAX_LOC(1), K:N+1) M (K-1+MAX_LOC(1), K:N+1) = TEMP_ROW( K:N+1) END IF IF (M (K, K) == 0) THEN FEL = .TRUE. ! Singulär matris A EXIT TRIANG_SLINGA ELSE TEMP_ROW (K+1:N) = M (K+1:N, K) / M (K, K) DO I = K+1, N M (I, K+1:N+1) = M (I, K+1:N+1) - & TEMP_ROW (I) * M (K, K+1:N+1) END DO M (K+1:N, K) = 0 ! Dessa värden användes ej END IF END DO TRIANG_SLINGA IF (M (N, N) == 0) FEL = .TRUE. ! Singulär matris A ! Återsubstitution IF (FEL) THEN X = 0.0 ELSE DO K = N, 1, -1 X (K) = ( M (K, N+1) - & SUM (M (K, K+1:N) * X (K+1:N)) ) / M (K, K) END DO END IF END SUBROUTINE LOES_LIN_EKV_SYSTEMIngående matris A och vektorer B och X har deklarerats med antaget mönster (assumed-shape array), dvs typ, rang och namn ges här, medan omfånget ges i anropande rutin, och då i ett explicit gränssnitt.
Notera att den inbyggda funktionen MAXLOC ger som resultat en heltalsvektor, med samma antal element som rangen (antalet dimensioner) hos argumentet. I vårt fall är argumentet en vektor, varför aktuell rang är ett och MAXLOC således ger en vektor med ett enda element. Detta är anledningen till att den lokala variabeln MAX_LOC deklarerats som en vektor med ett element. Om man deklarerar MAX_LOC som ett skalärt heltal fås kompileringsfel. Värdet blir naturligtvis index för det största elementet (det första av de största om flera är lika).
Notera även att numreringen börjar på 1 trots att jag tittar på en vektor med element som numreras från K till N. Jag har valt att inte utföra pivoteringen (dvs själva radbytet) om rutinen finner att raderna redan ligger rätt, nämligen när MAX_LOC(1) blir 1.
Beräkningen avbrytes så snart som singularitet konstateras, observera att denna kan inträffa så sent att den inte noteras inne i slingan.
Vid pivoteringen användes vektorn TEMP_ROW dels vid radbytet, dels utnyttjas den även för multiplikatorerna i Gausseliminationen.
Jag använder här tills vidare bara fältsektioner av vektortyp vid beräkningarna, men skall nu utnyttja funktionen SPREAD för att kunna använda fältsektioner av matristyp, varvid den innersta slingan försvinner (DO I = K+1, N).
Funktionen SPREAD (SOURCE, DIM, NCOPIES) returnerar ett fält av samma typ som argumentet SOURCE, men med rangen ökad med ett. Parametrarna DIM och NCOPIES är heltal. Om NCOPIES är negativ så användes i stället värdet noll. Om SOURCE är en skalär så blir SPREAD helt enkelt en vektor med NCOPIES element som alla har samma värde som SOURCE. Parametern DIM anger vilket index som skall utökas, det måste vara mellan 1 och 1+(rangen hos SOURCE), om SOURCE är en skalär måste således DIM vara ett. Parametern NCOPIES ger antalet element i den nya dimensionen, således ej antalet nya kopior. Om variabeln A svarar mot ett fält (/ 2, 3, 4 /) så blir
SPREAD (A, DIM=1, NCOPIES=3) SPREAD (A, DIM=2, NCOPIES=3) 2 3 4 2 2 2 2 3 4 3 3 3 2 3 4 4 4 4Jag går nu över till fältsektioner av matristyp, vilka kan kompileras effektivare på parallella maskiner, genom att byta ut den innersta explicita slingan, dvs
DO I = K+1, N M (I, K+1:N+1) = M (I, K+1:N+1) - & TEMP_ROW (I) * M (K, K+1:N+1) END DOmot
M (K+1:N, K+1:N+1) = M (K+1:N, K+1:N+1) & - SPREAD( TEMP_ROW (K+1:N), 2, N-K+1) & * SPREAD( M (K, K+1:N+1), 1, N-K)Anledningen till att vi måste "krångla till" det med funktionen SPREAD är att i den explicita slingan är (vid fixt I) variabeln TEMP_ROW(I) en konstant som multipliceras med N-K+1 olika värden M. Å andra sidan användes samma vektor ur M för alla värden på I, vilka är N-K stycken. Omformningen av matriserna skall ske till två matriser med samma mönster som delmatrisen M (K+1:N, K+1:N+1), dvs N-K rader och N-K+1 kolumner, eftersom alla de fyra räknesätten på fält är element för element.
Det kan tyvärr vara litet besvärligt att få parametrarna till SPREAD helt rätt. Till hjälp kan vara att utnyttja funktionerna LBOUND och UBOUND för att få lägre och övre dimensioneringsgränser, liksom att skriva ut det nya fältet med satsen
WRITE(*,*) SPREAD(SOURCE,DIM,NCOPIES)Notera då att utmatningen sker kolonnvis (dvs första index varierar snabbast, som vanligt i Fortran). Man kan använda de lägre och övre dimensioneringsgränserna för en mer explicit utmatningssats som ger en utmatning bättre anpassad till hur fältet ser ut. Här måste man dock först ha gjort en tilldelning till ett vanligt deklarerat fält med rätt mönster för att kunna utnyttja index på vanligt sätt. Notera vidare att indexen i en konstruktion som SPREAD automatiskt kanar ner till 1 som lägre gräns, även när man som SOURCE har något som A(4:7).
I den sista av DO-slingorna i programmet erhålles en tom summa vid index N, nämligen från N+1 till N. Enligt Fortrans regler blir denna summa noll, vilket är korrekt ur matematisk synpunkt. Om man har indexkontroll påslagen är det dock risk för att denna larmar. Jag har därför bytt ut
DO K = N, 1, -1 X (K) = ( M (K, N+1) - & SUM (M (K, K+1:N) * X (K+1:N)) ) / M (K, K) END DO END IFmot
X (N) = M (N, N+1) / M (N, N) DO K = N-1, 1, -1 X (K) = ( M (K, N+1) - & SUM (M (K, K+1:N) * X (K+1:N)) ) / M (K, K) END DO END IFi programmet loes1.f90 i källkodsbiblioteket.
(5.1) Jag byter ut
IF ( F(MITT) > 0.0 ) THEN V = MITT ELSE H = MITT END IFmot
IF ( F(MITT) == 0.0 ) THEN NOLL = MITT RETURN ELSE IF ( F(MITT) > 0.0 ) THEN V = MITT ELSE H = MITT END IF(5.2) En första ändring är att IF ( H - V >= EPS ) nu måste skrivas som det fullständiga IF ( ABS(H - V) >= EPS ) eftersom vi inte längre kan vara säkra på vilken punkt som är längst åt höger. En felutgång måste läggas in för det fall att F(A) och F(B) har samma tecken. I testen IF ( F(MITT) > 0.0 ) byter vi ut F(MITT) mot F(MITT)*F(V).
(5.3) Om man vill använda en mer avancerad algoritm kan man byta ut satsen MITT = 0.5*(H-V), man kan därvid fortfarande kalla den nya punkten för MITT, trots att den nu inte längre ligger mitt mellan V och H.
RECURSIVE FUNCTION TRIBONACCI(N) RESULT (T_RESULTAT) IMPLICIT NONE INTEGER, INTENT(IN) :: N INTEGER :: T_RESULTAT IF ( N <= 3 ) THEN T_RESULTAT = 1 ELSE T_RESULTAT = TRIBONACCI(N-1) + & TRIBONACCI(N-2) + TRIBONACCI(N-3) END IF END FUNCTION TRIBONACCIAnropande program kan skrivas
IMPLICIT NONE INTEGER :: N, M, TRIBONACCI N = 1 DO IF ( N <= 0 ) EXIT WRITE (*,*) ' GE N ' READ(*,*) N M = TRIBONACCI(N) WRITE(*,*) N, M END DO ENDoch ger resultatet TRIBONACCI(15) = 2209.
(5.5) Filen kvad.f90 för adaptiv numerisk kvadratur (integration). Jag använder trapetsformeln, halverar steget och gör Richardsonextrapolation. Metoden blir därför ekvivalent med Simpsons formel. Som feluppskattning använder jag Linköpingsmodellen, där felet högst är beloppet av skillnaden mellan de båda icke extrapolerade värdena. Om felet är för stort tillämpas rutinen var för sig på de båda delintervallen, varvid tillåtet fel i vart och ett av delintervallen blir hälften av det tidigare tillåtna.
RECURSIVE FUNCTION ADAPTIV_KVAD (F, A, B, TOL, ABS_FEL) & RESULT (RESULTAT) IMPLICIT NONE INTERFACE FUNCTION F(X) RESULT (FUNKTIONSVAERDE) REAL, INTENT(IN) :: X REAL :: FUNKTIONSVAERDE END FUNCTION F END INTERFACE REAL, INTENT(IN) :: A, B, TOL REAL, INTENT(OUT) :: ABS_FEL REAL :: RESULTAT REAL :: STEG, MITT REAL :: EN_TRAPETS_YTA, TVAA_TRAPETS_YTA REAL :: VAENSTER_YTA, HOEGER_YTA REAL :: DIFF, ABS_FEL_V, ABS_FEL_H STEG = B - A MITT = 0.5 * (A + B) EN_TRAPETS_YTA = STEG * 0.5 * (F(A) + F(B)) TVAA_TRAPETS_YTA = STEG * 0.25 * (F(A) + F(MITT)) + & STEG * 0.25 * (F(MITT) + F(B)) DIFF = TVAA_TRAPETS_YTA - EN_TRAPETS_YTA IF ( ABS(DIFF) < TOL ) THEN RESULTAT = TVAA_TRAPETS_YTA + DIFF/3.0 ABS_FEL = ABS(DIFF) ELSE VAENSTER_YTA = ADAPTIV_KVAD (F, A, MITT, & 0.5*TOL, ABS_FEL_V) HOEGER_YTA = ADAPTIV_KVAD (F, MITT, B, & 0.5*TOL, ABS_FEL_H) RESULTAT = VAENSTER_YTA + HOEGER_YTA ABS_FEL = ABS_FEL_V + ABS_FEL_H END IF END FUNCTION ADAPTIV_KVADFilen test_kva.f90 för test av ovanstående rutin för adaptiv numerisk kvadratur behöver INTERFACE både för funktionen F och för kvadraturrutinen ADAPTIV_KVAD. Notera att för den senare måste funktionen F deklareras både med typ REAL och med EXTERNAL.
Jag har valt att komplettera detta program med mätning av exekveringstiden, utnyttjande de i Fortran 90 inbyggda subrutinerna för detta, se Appendix 5, avsnitt 21.
PROGRAM TEST_ADAPTIV_KVAD IMPLICIT NONE INTERFACE FUNCTION F(X) RESULT (FUNKTIONSVAERDE) REAL, INTENT(IN) :: X REAL :: FUNKTIONSVAERDE END FUNCTION F END INTERFACE INTERFACE RECURSIVE FUNCTION ADAPTIV_KVAD & (F, A, B, TOL, ABS_FEL) RESULT (RESULTAT) REAL, EXTERNAL :: F REAL, INTENT(IN) :: A, B, TOL REAL, INTENT(OUT) :: ABS_FEL REAL :: RESULTAT END FUNCTION ADAPTIV_KVAD END INTERFACE REAL :: A, B, TOL REAL :: ABS_FEL REAL :: RESULTAT, PI INTEGER :: I INTEGER :: COUNT1, COUNT2, COUNT_RATE REAL :: TID PI = 4.0 * ATAN(1.0) A = -5.0 ; B = +5.0 TOL = 0.1 CALL SYSTEM_CLOCK(COUNT=COUNT1, COUNT_RATE=COUNT_RATE) DO I = 1, 5 TOL = TOL/10.0 RESULTAT = ADAPTIV_KVAD (F, A, B, TOL, ABS_FEL) WRITE(*,*) WRITE(*,"(A, F15.10, A, F15.10)") & "Integralen är approximativt ", & RESULTAT, " med approximativ feluppskattning ", & ABS_FEL WRITE(*,"(A, F15.10, A, F15.10)") & "Integralen är mer exakt ", & SQRT(PI), " med verkligt fel ",& RESULTAT - SQRT(PI) END DO CALL SYSTEM_CLOCK(COUNT=COUNT2) TID = REAL(COUNT2 - COUNT1)/REAL(COUNT_RATE) WRITE(*,*) ' Beräkningen tar ', TID, ' sekunder' END PROGRAM TEST_ADAPTIV_KVADVi får naturligtvis inte glömma bort integranden, vilken jag dock låter ligga i samma fil som huvudprogrammet. Deklarationen är av den nya typen, speciellt vad gäller att resultatet returneras i en särskild variabel.
FUNCTION F(X) RESULT (FUNKTIONSVAERDE) IMPLICIT NONE REAL, INTENT(IN) :: X REAL :: FUNKTIONSVAERDE FUNKTIONSVAERDE = EXP(-X**2) END FUNCTION FNu är det dags att provköra på Sun-datorn. Jag har redigerat utmatningen något för att få den mer koncentrerad. Feluppskattningen är relativt realistisk i varje fall med denna integrand, den onormaliserade felfunktionen.
Huvudprogrammet och funktionen f(x) finns i filen test_kva.f90, medan den rekursiva funktionen finns i kvad.f90 Dessa kan hämtas med WWW och kan därför enkelt användas för egna tester.
f90 test_kva.f90 kvad.f90 test_kva.f90: kvad.f90: a.out Integralen är 1.7733453512 med feluppskattning 0.0049186843 med verkligt fel 0.0008914471 Integralen är 1.7724548578 med feluppskattning 0.0003375171 med verkligt fel 0.0000009537 Integralen är 1.7724541426 med feluppskattning 0.0000356939 med verkligt fel 0.0000002384 Integralen är 1.7724540234 med feluppskattning 0.0000046571 med verkligt fel 0.0000001192 Integralen är 1.7724539042 med feluppskattning 0.0000004876 med verkligt fel 0.0000000000Jag har kört programmet på ett antal olika system och fått följande resultat. Vid upprepad körning varierar körtiden något.
Dator | Tid | Precision |
---|---|---|
sekunder | decimala siffror | |
PC Intel 486 SX 25 | 74,8 | 6 |
PC Intel 486 DX 50 | 2,75 | 6 |
PC Intel Pentium 200 | 0,26 | 6 |
Sun SPARC SLC | 2,50 | 6 |
Sun SPARC station 10 | 0,58 | 6 |
Cray Y-MP | 0,19 | 14 |
Cray C 90 | 0,13 | 14 |
DEC Alpha 3000/900 | 0,06 | 6 |
DEC Alpha 3000/900 | 0,06 | 15 |
DEC Alpha 3000/900 | 3,32 | 33 |
Anm. Ovanstående program är skrivet på ett mycket direkt sätt för att illustrera rekursiv och adaptiv teknik. Det är därför inte optimerat, en enkel optimering föreslås i övning (22.14).
SUBROUTINE SOLVE(F, A, B, TOL, EST, RESULTAT) REAL, EXTERNAL :: F REAL, OPTIONAL, INTENT (IN) :: A REAL, OPTIONAL, INTENT (IN) :: B REAL, OPTIONAL, INTENT (IN) :: TOL REAL, INTENT(OUT), OPTIONAL :: EST REAL, INTENT(OUT) :: RESULTAT IF (PRESENT(A)) THEN TEMP_A = A ELSE TEMP_A = 0.0 END IF IF (PRESENT(B)) THEN TEMP_B = B ELSE TEMP_B = 1.0 END IF IF (PRESENT(TOL)) THEN TEMP_TOL = TOL ELSE TEMP_TOL = 0.001 END IF ! Här skall den verkliga beräkningen finnas, men den ! är här ersatt med enklast tänkbara approximation, ! nämligen mittpunktsapproximation utan feluppskattning. RESULTAT = (TEMP_B - TEMP_A) & * F(0.5*(TEMP_A+TEMP_B)) IF (PRESENT(EST)) EST = TEMP_TOL RETURN END SUBROUTINE SOLVEDen förenklade integralberäkningen ovan kan exempelvis ersättas med den adaptiva kvadraturen i övning (5.5).
INTERFACE SUBROUTINE SOLVE (F, A, B, TOL, EST, RESULTAT) REAL, EXTERNAL :: F REAL, INTENT(IN), OPTIONAL :: A REAL, INTENT(IN), OPTIONAL :: B REAL, INTENT(IN), OPTIONAL :: TOL REAL, INTENT(OUT), OPTIONAL :: EST REAL, INTENT(OUT) :: RESULTAT END SUBROUTINE SOLVE END INTERFACE(9.1) Variablerna A och B tilldelas angivna värden, men hela resten av raden blir kommentar!
(9.2) Nej, på den andra raden är det blanka tecknet efter & ej tillåtet. Det bryter nämligen identifieraren ATAN i två. Om det blanka tecknet tas bort blir raderna korrekta. Fri form förutsättes, eftersom & ej är fortsättningstecken i fix form.
INTEGER, PARAMETER :: DP = SELECTED_REAL_KIND(15,99)(10.2)
REAL (KIND=DP) :: E, PI(10.3)
REAL (KIND=DP), PARAMETER :: E = 2.718281828459045_DP, & PI = 3.141592653589793_DP(12.1) Jag antar att vektorn har en fix dimensionering, samt gör en kontrollutmatning av ett par värden.
REAL, TARGET, DIMENSION(1:100) :: VEKTOR REAL, POINTER, DIMENSION(:) :: UDDA, JAEMNA UDDA => VEKTOR(1:100:2) JAEMNA => VEKTOR(2:100:2) JAEMNA = 13 ; UDDA = 17 WRITE(*,*) VEKTOR(11), VEKTOR(64) END(12.2) Jag antar att den givna vektorn även här har en fix dimensionering.
REAL, TARGET, DIMENSION(1:10) :: VEKTOR REAL, POINTER, DIMENSION(:) :: PEKARE1 REAL, POINTER :: PEKARE2 PEKARE1 => VEKTOR PEKARE2 => VEKTOR(7)(14.1) Använd funktionerna MIN och MAX för att finna minsta respektive största värde av alla kombinationer A%LAEGRE * B%LAEGRE, A%LAEGRE * B%OEVRE, A%OEVRE * B%LAEGRE och A%OEVRE * B%OEVRE vid multiplikation, och motsvarande vid division.
(14.2) Testa om B%LAEGRE <= 0 <= B%OEVRE, i vilket fall felutskrift skall ske.
(14.3) Eftersom vi inte har direkt tillgång till maskinaritmetiken (dvs kommandon av typ avrunda nedåt respektive avrunda uppåt) så kan man få en hyfsad simulering genom att minska respektive öka med avrundningskonstanten. I princip dubbleras då effekten från avrundningen.
(16.1) Om detta misslyckas är det troligen fel redan i det ursprungliga Fortran 77 programmet, eller Du har utnyttjat någon utvidgning från standard Fortran 77.
(16.2) Om detta misslyckas beror det troligen på att felaktiga kommandon tolkades som variabler under fix form, men nu när blanka är signifikanta upptäcks den felaktiga syntaxen.
Notera även att eventuell text i positionerna 73 till 80 under fix form normalt behandlas som kommentar (till exempel radnummer).
(16.3) Fortran 77 ger inget fel varken under kompilering eller exekvering. Kompilering under Fortran 90 fix form ger en varning från kompilatorn att variabeln ZENDIF användes utan att ha tilldelats något värde. Programmet tolkas nämligen så att THENZ, ELSEY och ZENDIF blir vanliga flyttalsvariabler. Kompilering under Fortran 90 fri form ger däremot ett antal syntaxfel. Den rätta versionen av programmet skall nämligen innehålla tre ytterligare vagnreturer, se nedan.
LOGICAL L L = .FALSE. IF (L) THEN Z = 1.0 ELSE Y = Z END IF ENDAnmärkning. Även vissa Fortran 77 kompilatorer ger varning om variabeln ZENDIF.
(22.1) Under fix form betyder den LOGICAL L, dvs variabeln L deklareras som logisk. Under fri form blir det syntaxfel.
REAL,PARAMETER :: K = 0.75
INTEGER, DIMENSION(3,4) :: PELLE
INTEGER, PARAMETER :: DP = SELECTED_REAL_KIND(15,99)
REAL (KIND=DP) :: E, PI
E = 2.718281828459045_DP ; PI = 3.141592653589793_DP
(22.7) Nej, den är inte korrekt eftersom ett kommatecken fattas mellan REAL och DIMENSION. I den form som den skrivits tolkas satsen som en deklaration (gamla typen) av flyttalsmatrisen DIMENSION och en implicit deklaration (nya typen) av ett skalärt flyttal AA. Strikt formellt är det dock en korrekt deklaration. Variabelnamnet DIMENSION är tillåtet under Fortran 90, men det är naturligtvis för långt under Fortran 77.
(22.8) Ja, den är korrekt men den är mindre lämplig eftersom den dödar den inbyggda funktionen REAL för explicit konvertering av en variabel av annan typ till typen REAL. Det är dock inget som hindrar att man har en variabel av typ REAL med namnet REAL, eftersom Fortran inte har reserverade ord.
(22.9) Nej, den är inte korrekt, vid COMMON användes inte dubbelkolon vid deklaration, i princip eftersom COMMON är ett utgående kommando. Den rätta deklarationen är den gamla hederliga
COMMON A
(22.10) Satsen är ej tillåten, vilket dock visar sig först vid exekvering. Man kan i stället skriva
WRITE(*,*) ' HEJ 'eller
WRITE(*,'(A)') ' HEJ 'vilka båda skriver ut texten HEJ på standardenheten för utmatning. Om man vill ge den text man vill mata ut direkt på den plats där utmatningsformatet skall ges kan man antingen använda apostrofeditering
WRITE(*, "(' HEJ ')")eller den föråldrade Hollerith-räknaren
WRITE(*, "(5H HEJ )")
(22.11) De skriver ut till beloppet stora eller små tal med en heltalssiffra, sex decimaler och exponent, medan tal mittemellan skrivs på ett naturligt sätt. Vi får således i detta fall
1.000000E-03 1.00000 1.000000E+06Talen från 0,1 till 100 000 skrivs ut på det naturliga sättet, och med sex signifikanta siffror.
SELECT CASE (N) CASE(:-1) ! Fall 1 CASE(0) ! Fall 2 CASE(3,5,7,11,13) ! Fall 3 END SELECT(22.13)
SUMMA = 0.0 DO I = 1, 100 IF ( X(I) == 0.0 ) EXIT IF ( X(I) < 0.0 ) CYCLE SUMMA = SUMMA + SQRT(X(I)) END DOOm man i stället använder en FORALL-konstruktion måste man notera att i DO-satsen tolkas "aktuellt" värde som det första med angiven egenskap (nämligen noll), varvid beräkningen skall avbrytas enligt specifikationen av uppgiften. Detta fungerar då inte vid den mer parallella FORALL-konstruktionen, som är olämplig i detta fall.
Anm. Uppgiften är inte helt entydig, eftersom man kan tänkas utföra DO-satsen i annan ordning!
(22.14) Denna övning består i att optimera det adaptiva programmet för numerisk kvadratur (se Övning (5.5) och dess lösning) genom att minska antalet funktionsanrop av funktionen f(x).
Vi noterar att vid varje anrop av den rekursiva funktionen beräknas funktionsvärdet i de båda ändpunkterna samt i mittpunkten (det senare till och med två gånger)!
Det modifierade huvudprogrammet och funktionen f(x) finns i filen test_kv2.f90, medan den modifierade rekursiva funktionen finns i kvad2.f90 Även dessa kan hämtas med WWW och kan därför enkelt användas för egna tester.
Följande ändringar har gjorts: