Yleistä ohjelmointikielistä

Tietokone ymmärtää vain omaa konekieltään.
Konekielinen ohjelma on jono binäärilukuja, joista koostuvia käskyjä prosessori tulkitsee.

Symbolinen konekieli (assembler) vastaa yksikäsitteisesti konekielistä ohjelmaa, mutta on selväkielistä:

         LD  A 
         ADD B
         MUL C
         STO A
Korkean tason kieli vastaa matemaattista notaatiota tai luonnollista kieltä. (Fortran, Lisp, Cobol, Algol, Pascal, C, Java, ...)
         A = (A+B)*C
Sovelluskehittimet tuottavat ohjelman kuvauksesta, joka kirjoitetaan hyvin tehtävänläheisellä kielellä.

Kääntäjä (compiler) on ohjelma, joka muuntaa korkean tason kielellä kirjoitetun ohjelman konekieliseksi, suoritettavaksi ohjelmaksi.


Yksinkertaiset muuttujat

Pienin suoraan osoitettavissa oleva yksikkö yleensä 8 bitin tavu (poikkeuksena sanakoneet).

Kokonaisluku (integer) tavallisesti 2 tavua. Kokonaisluvun itseisarvo voi olla korkeintaan 215-1=32767.

Usein käytettävissä myös kaksoistarkkuuden kokonaisluku, 4 tavua, jolloin suurin esitettävissä oleva luku on 231-1=2147483647.

Reaaliluku (real) tavallisesti 4 tavua
- mantissan etumerkki
- mantissa normitettu välille 0.1 - 1
- eksponentti: etumerkki+itseisarvo tai 2:n komplementti

Esim. PC:n yksinkertaisen tarkkuuden reaaliluvun mantissa on 23 bittiä. Desimaalipisteen jälkeinen bitti on aina ykkönen, joten sitä ei tarvitse tallettaa. Tarkkuus on 24 bittiä eli log_10 224 \approx 7 desimaalia.
Eksponenttiosan pituus 8 bittiä. Eksponenttiosa e = 127 (bias) + todellinen eksponentti; 1\le e \le 254. Lukualue 2-126\approx 10-38 - 2128\approx 3x1038.

Kaksoistarkkuus: merkitsevien numeroiden määrä noin kaksinkertainen ja mahdollisesti myös lukualue laajempi. Esimerkiksi PC:n kaksoistarkkuuden muuttujien tarkkuus on noin 15 desimaalia ja lukualue noin 10-308 ... 10308.

Looginen muuttuja (logical) voi saada vain arvot tosi tai epätosi.

Osoitin (pointer)
- osoitetun muuttujan osoite muistissa + muuta tietoa

Strukturoidut muuttujat

Yksinkertaisista muuttujista voidaan muodostaa mutkikkaampia tietorakenteita.

Taulukot (array)

Tietueet (record, structure)


Kontrollirakenteet

1 Peräkkäisyys
 
        S1
        S2
        S3

2 Valinta (1, 2 tai useita vaihtoehtoja)
        if ehto then S1
        if ehto then S1 else S2 
        case ehto { S1; S2; ... }
3 Toisto
        while ehto do S
        do S until ehto
        for i=1,n do S
Kaikki toistolauseet ovat erikoistapauksia n+1/2 kierroksen silmukasta
        do
        { 
          S1
          if ehto exit
          S2
        }

Kaikki ohjelmointitehtävät voidaan toteuttaa em. rakenteilla.

Useissa kielissä lisäksi hyppykäsky (go to)
- yleensä tarpeeton
- sopii lähinnä poikkeustilanteisiin
- vaikeuttaa ohjelman luettavuutta
- kontrollireitit mutkistuvat
- osoitteesta ei näy, mistä siihen on tultu


Aliohjelmat

- tehtävän jäsentely pienempiin ja helpommin hallittaviin osiin
- toiston välttäminen
- testauksen helpottaminen
- aliohjelmakirjastot


Fortranin historiaa


Fortran 90/95

+sopii erityisesti numeriikkaan:
+ optimoivat kääntäjät -> tehokas koodi
+ mukana valmiiksi paljon varusfunktioita
+ kompleksiluvut
+ taulukko-operaatiot
+ operaattorit laajennettavissa myös omille tietotyypeille

+ standardoitu -> hyvä siirrettävyys

+ saatavana hyviä aliohjelmakirjastoja (NaG yms.)

+ kääntäjät hyväksyvät myös vanhat ohjelmat, joita on liikkeellä paljon

- useimmat vanhoista ohjelmista hirveitä sekasotkuja

- kivikautisia sudenkuoppia, joita opittava varomaan


Esimerkki: Lähdekielinen ohjelma tiedostossa add.f90:

     program add
     real x,y,z
     write(*,*) 'anna kaksi lukua'
     read (*,*) x,y
     z=x+y
     write(*,*) 'summa on',z
     end program add
Käännös ja suoritus (esimerkiksi):
     >f95 -o add add.f90
     Compiling file add.f90.
     Compiling program unit add at line 1:
     >
     >./add
     anna kaksi lukua
     1,3
     summa on 4.00000000
     >

Esimerkki: Yksinkertainen yhtälön ratkaisija

Kirjoitetaan yhtälö muotoon x=f(x). Esimerkiksi x5 - x - 1 = 0 ->

x = x5 - 1

tai

x = (1+x)0.2

        program solve
        ! etsitaan yhtalon x**5-x-1=0
        ! reaalijuuri
        real x0, x1
   
        x0 = 0.5         ! arvataan alkuarvo
        x1=(1.0+x0)**0.2
        ! iteroidaan, kunnes tulos ei muutu
        do while (x1.ne.x0)
           x0 = x1
           x1 = (1.0+x0)**0.2
        end do
        write (6, *) x1, x1**5-x1-1
        end program solve
     >f95 -o solve solve.f90
     Compiling file solve.f90.
     Compiling program unit solve at line 1:
     >./solve
     1.16730404 5.03132583E-07


Ohjelman ulkoasu

Kiinteä ja vapaa muoto, joita ei saa käyttää sekaisin. Fortran 77:ssa vain kiinteä muoto; seuraavassa käsitellään vain vapaata muotoa.

Pienillä ja isoilla kirjaimilla ei eroa (paitsi mahdollisesti tiedostojen nimissä käyttöjärjestelmästä riippuen)

Lause päättyy rivin loppuun; erityistä erotinta ei tarvita.

Jos lause jatkuu useammalle riville, se on erikseen osoitettava. Rivin lopussa oleva &-merkki ilmoittaa, että lause jatkuu seuraavalla rivillä:

        y = 1.0 + x + 0.5 * x**3 &
          + 1.0/6 * x**4
Lauseen maksimipituus 40 riviä.

Rivin loppu huutomerkistä eteenpäin on kommenttia


Yksinkertaiset muuttujat

Yksinkertaisilla muuttujilla on implisiittinen tyyppi:

- integer, jos ensimmäinen kirjain on I - N
- real muuten

Vaarallista! Väärin kirjoitettu muuttujan nimi tulkitaan vain eri muuttujaksi.

        x0=1.0
        x1=xO+1
Määrittele kaikki muuttujat!
Estä implisiittisten määrittelyjen käyttö:
 
        implicit none
Yksinkertaiset tyypit:
        integer
        real
        logical  (arvo .true. tai .false. )
        complex
        double precision
        character
Vakiot:
        integer, parameter :: maxn=1000
        real, parameter :: pi=3.141592654
maxn ja pi vakioita, joiden arvoja ohjelma ei saa muuttaa


Sijoitusoperaattori =

        i=100
        x=1.5

Lausekkeet

        1.0+2.0*y/z**2-3.5*(y+x)
Normaali assosiatiivisuus:
- ensin ** (potenssiinkorotus)
- sitten * ja / vasemmalta oikealle
- lopuksi + ja - vasemmalta oikealle
- järjestystä voidaan muuttaa suluilla

Ensin lasketaan sijoitusoperaattorin = oikealla puolella oleva lauseke, se muunnetaan vasemmalla olevan muuttujan tyyppiseksi ja talletetaan muuttujaan

        real x
        x = 1/2   ! x=?
Kokonaislukujen jakolaskun tulos on kokonaisluku (osamäärän kokonaisosa)
        x=1.0/2


Varusfunktiot (intrinsic functions)

Trigonometriset funktiot (kulmat aina radiaaneina!):

        sin(x), cos(x), tan(x)
        asin(x), acos(x), atan(x), atan2(y,x)
Hyperboliset funktiot:
        sinh(x), cosh(x), tanh(x)
Eksponentti, logaritmi ym.
        exp(x), log(x), log10(x), sqrt(x)
Minimi ja maksimi; argumenttien määrä mielivaltainen:
        min(x, y, ...), max(x, y, ...)
Itseisarvo
        abs(x)
Esim. muunnos pallokoordinaatistoon
        real x,y,z,r,phi,theta
        real, parameter :: pi=3.141592654
        x=-1.0 ; y=3.0; z=2.0
        r=sqrt(x**2+y**2+z**2)
        phi=atan2(y,x)*180.0/pi
        theta=asin(z/r)*180.0/pi


Vertailuoperaattorit

        ==    .eq.
        /=    .ne.
        <     .lt.
        <=    .le.
        >     .gt.
        >=    .ge.
Huom: = on sijoitusoperaattori; vertailu on ==.
        integer n
        logical d
        d = n == 100*(n/100)     ! tosi, jos n jaollinen 100:lla 
        d = n .eq. 100*(n/100)
Loogiset vakiot
 
        .true.  .false.
Loogiset operaattorit
        .and.   
        .or.    
        .not.   
X.and.Y on tosi, jos ja vain jos X==.true. ja Y==.true..

X.or.Y on tosi, jos X==.true. tai Y==.true. tai molemmat ovat tosia.

.not.X on tosi, jos {\tt X==.false.}.

        logical leap, d4, d100, d400
        integer y
         ...
        d4 = y==4*(y/4)
        d100 = y==100*(y/100)
        d400 = y==400*(y/400)
        leap = d4.and.(.not.d100 .or. d400)


Peruskontrollirakenteet: Peräkkäisyys

Tavallisesti kukin lause omalla rivillään, jolloin ei tarvita mitään erotinta:
        x=1.0
        y=exp(-x**2/2)
        z=1-y
Samalla rivillä voi olla useita lauseita, jotka erotetaan puolipisteellä:
        x = 1.0 ;  y = 2.0 ;  z = 0.1


Peruskontrollirakenteet: Valinta

Yksi vaihtoehto
       if (x > 0.0) y = 1/x

       if (x > 0.0 .and. x < 100.0) y=exp(x)

       if (x > 0.0) then
          y=1/x
          z=log(x)
       end if 
Lause(et) suoritetaan vain, jos ehto on voimassa, muulloin ei tehdä mitään

Kaksi vaihtoehtoa

        if (x > 0.0) then
           y=1/x
        else
           y=0.0
        end if

Useampia vaihtoehtoja

        if (x > 0.0) then
           y=log(x)
        else if (x < 0.0) then
           y=-log(abs(x))
        else
           y=0.0
        end if


Peruskontrollirakenteet: Toisto

Kiinteä kierrosmäärä
        sum=0.0
        do i=1,100
           sum=sum+i
        end do
        sum=0.0
        do i=0,100,2  ! parillisten lukujen summa
           sum=sum+i
        end do


Numeriikkaa: Äärellinen laskentatarkkuus

Konevakiot ovat laskentatarkkuutta ja liukulukujen esitystapaa kuvaavia lukuja.

"Kone-epsilon" on pienin luku, joka ykköseen lisättynä antaa ykköstä suuremman tuloksen:

epsilon= min { x | 1+x > 1 }.

PC:n kaksoistarkkuuden lukuja vastaava konevakio on epsilon=2.2 x 10-16. Huom! tämä on hyvin paljon suurempi kuin pienin esitettävissä oleva positiivinen luku.


Virheet

Jos luvun tarkka arvo on a, sen likimääräisen esityksen ã absoluuttinen virhe on

Delta a = ã - a.

Suhteellinen virhe on

e= (Delta a / a) = (ã - a) / a.

Usein a ei ole tiedossa, mutta Delta a tunnetaan esimerkiksi tilastollisten ominaisuuksien avulla. Arvio suhteelliselle virheelle on silloin

e ~ (Delta a / ã).

Pyöristys:

          1.490         1, 1.5 
          1.551         2, 1.6 
          1.500         2 
          2.500         2
Katkaisussa luvun loppuosa heitetään menemään.

Fortranissa muunnos reaaliluvusta kokonaisluvuksi tehdään katkaisemalla.

Yhteenlasku

Yhteenlaskussa lasketaan yhteen myös lukujen virheet. Jos virheiden merkit ovat satunnaisia, virheet osittain kumoavat toisensa.

1.57 + 0.76 = 2.33.

Lasketaan summa pyöristämällä luvut yhteen desimaaliin:

1.6+0.8=2.4

Termien suhteelliset virheet ovat

(1.6-1.57)/1.57 = 0.019, (0.8-0.76)/0.76 = 0.053.

Summan suhteellinen virhe on

(2.4-2.33)/2.33 = 0.030.

Summan suhteellinen virhe ei koskaan voi olla suurempi kuin suurin yksittäisten positiivisten termien suhteellisista virheistä.

Muutetaan esimerkkiä hieman:

1.57 + 0.74 = 2.31.

1.6+0.7=2.3

Termien suhteelliset virheet ovat

(1.6-1.57)/1.57 = 0.019, (0.7-0.74)/0.74 = -0.054.

Summan suhteellinen virhe on

(2.3-2.31)/2.31 = -0.004.

Yhteenlasku on virheitä tasoittava operaatio, mikäli yksittäisten termien virheiden merkit ovat satunnaisia.

Ongelmia samanmerkkisten lukujen yhteenlasku voi aiheuttaa, kun lasketaan yhteen hyvin eri suuruisia lukuja. Jos esitystarkkuus on 7 desimaalia, on 1.0+3x10-8=1.0.

Vähennyslasku

pi_1=3.160494 ja pi_2=3.142857. Esitetään luvut kolmella desimaalilla, ja vähennetään toisistaan:
              tarkka arvo likiarvo abs. virhe suht. virhe
 pi_1           3.160494    3.160   -0.00049   -0.00016
 pi_2           3.142857    3.143    0.00014    0.00005
 pi_1-pi_2      0.017637    0.017  - 0.00064   -0.03612
Erotuksen suhteellinen virhe on paljon suurempi kuin alkuperäiset virheet, sillä mantissojen merkitsevimmät numerot kumoavat toisensa ja jäljelle jää alkuperäistä vähemmän merkitseviä numeroita (catastrophic cancellation).

Varo lähes yhtäsuurten lukujen vähennyslaskua!

Ongelmia aiheuttaa esimerkiksi

sqrt(1+x)-1,

kun x on pieni. Mikäli x<<1, voidaan neliöjuuri korvata Taylor-sarjan alkupäällä

sqrt(1+x)-1 ~ 1+(1/2)x-1 = (1/2)x.

Lauseke voidaan myös muuntaa toiseen muotoon

sqrt(1+x)-1 = x/(1+sqrt(1+x)).


Esimerkki: lasketaan integraali

In=\int01 xn/(x+10) dx

In+1+10In = \int01 (xn+1/(x+10)+10xn/(x+10)) dx
= \int01 (xn(x+10)/(x+10) dx
= \int01 xn dx = 1/(n+1),

josta saadaan palautuskaava

In+1 = 1/(n+1) - 10In.

I_0=\int01 dx/(x+10) = ln 11 - ln 10 ~ 0.0953.

  I_1 = (1/1) - 10 x 0.0953 = 0.0470,
  I_2 = (1/2) - 10 x 0.0470 = 0.0300,
  I_3 = (1/3) - 10 x 0.0300 = 0.0333,
  I_4 = (1/4) - 10 x 0.0333 = -0.0833. ????
Nimittäjä on likimain vakio 10, joten

In ~ (1/10) \int01 xn dx = (1/10)(1/(n+1)).