Skip to main content Link Menu Expand (external link) Document Search Copy Copied

Kokonaisluvut, liukuluvut ja rakenteinen tieto

Kaikki tieto (data) tietokoneessa koodataan biteillä (0 ja 1), joista muodostettuja binäärijonoja kutsutaan binääriluvuiksi. Binäärilukujärjestelmä on itsessään yksikäsitteinen. Esimerkiksi binääriluku 0b01111111 on kymmenjärjestelmän luku 127. Mutta tietokoneen laitteiston käsitellessä lukuja sen arvo voi olla jokin muu. Esimerkiksi vakiolisäys 127 -muodossa tallennettu binääriluku 0b01111111 onkin luku 0.

Tässä osassa perehdytään tarkemmin eri tapoihin, joilla kokonaisluvut ja liukuluvut (reaalilukujen esitysmuoto tietokoneessa) ilmaistaan ja millaisessa muodossa niitä käsitellään suorittimella ja tallennetaan muistiin. Lisäksi perehdytään rakenteisen tiedon tallentamiseen 1-ulotteisiin ja 2-ulotteisiin taulukoihin. Tässä materiaalissa ei tällä hetkellä ole tietoa merkkijonojen ja totuusarvojen tallentamisesta eikä tietueista. Niistä voitte lukea tarkemmin Tietokoneen toiminnan perusteet -kurssin luvusta 3. Tässä osassa ei myöskään perehdytä siihen, miten lukuja ja merkkejä näytetään ihmisen luettavassa muodossa vaikkapa tietokoneen näytöllä.

Käsitteellä muisti (memory) tarkoitetaan yleensä keskusmuistia (main memory), ellei asiayhteydestä selviä sen tarkoittavan yleisesti erilaisia muisteja kuten välimuistia tai massamuistia.

Kokonaislukujen esitysmuodot

Binääriluvut itsessään ovat aina ei-negatiivisia. Pienin luku on 0 ja suurin luku lähestyy ääretöntä, aivan samoin kuin kymmenjärjestelmänkin ei-negatiivissa kokonaisluvuissa.

Tietokoneen käsitellessä lukuja äärellisen kokoisissa laskentayksiköissä ja siirtäessä dataa esimerkiksi suorittimen ja muistin välissä, on lukujen suuruudella kuitenkin rajoitteita. Lisäksi on tarpeellista pystyä ilmaisemaan myös negatiivisia kokonaislukuja.

Tietokoneen käsitellessä kokonaislukuja esitysmuotoon vaikuttavat seuraavat asiat:

1. Luvun pituus

  • Esimerkiksi 8-bittinen tai 32-bittinen
  • Luvun pituus on huomioitava sekä tietoa tallennettaessa että luettaessa.

2. Positiivinen vai negatiivinen

Etumerkillinen (sign and magnitude)

  • Eniten merkitsevä bitti ilmaisee merkin (0 on + ja 1 on -)
  • Loput bitit ilmaisevat luvun suuruuden
  • Vastaluku saadaan kääntämällä eniten merkitsevä bitti
  • Ihmiselle helppo ja luontainen tapa merkin ilmaisuun
  • Laskentapiireille monimutkaisempi laskenta kuin esim. kahden komplementissa

Yhden komplementti

  • Positiivinen luku ilmaistaan tavallisena binäärilukuna
  • Vastaluku saadaan kääntämällä luvun bitit (0 ➝ 1 ja 1 ➝ 0)
    • Negatiivinen luku saadaan kääntämällä positiivisen luvun bitit
    • Negatiivisen luvun itseisarvo saadaan kääntämällä negatiivisen luvun bitit
  • Nopeaa muuntaa
  • Ei kuitenkaan tehokasta laskutoimitusten kannalta
  • Kaksi esitysmuotoa luvulle 0 (positiivinen ja negatiivinen 0)

Kahden komplementti

  • Positiivinen luku ilmaistaan tavallisena binäärilukuna
  • Vastaluku saadaan kääntämällä luvun bitit (0 ➝ 1 ja 1 ➝ 0) ja sen jälkeen lisäämällä luku 1
    • Negatiivinen luku saadaan kääntämällä positiivisen luvun bitit ja lisäämällä 1
    • Negatiivisen luvun itseisarvo saadaan kääntämällä negatiivisen luvun bitit ja lisäämällä 1
  • Yleisin tapa ilmaista kokonaislukuja
  • Laskutoimitukset tehokkaasti toteutettavissa logiikkapiireillä
  • Vain yksi 0

Vakiolisäys

  • Ilmaistavaan lukuun lisätään jokin vakio, jotta luvun voi tallettaa ei-negatiivisena lukuna
  • Esimerkiksi tavun mittaisessa luvussa voidaan vakiolisäyksellä 127 saada muunnettua luvut -127…128 tallennusmuotoon 0…255.
  • Etuna mm. suuruusvertailun helppous: luvut ovat samassa suuruusjärjestyksessä sekä tallennusmuodossaan että alkuperäisessä muodossaan (toisin kuin yllämainituissa)

3. Tavujärjestys

Big Endian

  • Luonnollinen tavujärjestys, jossa eniten merkitsevä tavu on vasemmalla, seuraavana toiseksin merkitsevin tavu ja niin edelleen.

Little Endian

  • Käänteinen tavujärjestys
  • Huom: Kyse on tavujärjestyksestä, ei puolitavujärjestyksestä tai bittijärjestyksestä!

Tarkastellaan seuraavaksi tarkemmin näitä esitysmuotoja esimerkkien avulla.

Etumerkillinen (sign and magnitude)

Etumerkillisessä esitysmuodossa luku ilmaistaan normaalina binäärilukuna, mutta eniten merkitsevä bitti ilmaisee luvun merkin. Se on 0, jos luku on positiivinen ja 1, jos luku on negatiivinen. Merkkiä varten on siis yksi bitti varattuna ja loput bitit ilmaisevat luvun itseisarvon. Jos luvun pituus on vaikkapa 32 bittiä, itseisarvoa varten on käytössä 31 bittiä.

Kymmenjärjestelmän luvun muuntaminen etumerkilliseksi luvuksi

Esimerkki: Luvun \(-266\) muuntaminen 32-bittiseksi etumerkilliseksi luvuksi

Muutetaan ensin luvun \(-266\) itseisarvo \(266\) binääriluvuksi ja saadaan:

\[0\text{b}\hspace{0.2em}1\hspace{0.2em}0000\hspace{0.2em}1010\]

Ilmaistaan luku halutussa pituudessa eli 32-bittisenä ja saadaan:

\[0\text{b}\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0001\hspace{0.2em}0000\hspace{0.2em}1010\]

Sen jälkeen muutetaan eniten merkitsevä bitti (vasemmanpuoleisin bitti) yhdeksi, koska luvun pitää olla negatiivinen:

\[0\text{b}\hspace{0.2em}\mathbf{1}000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0001\hspace{0.2em}0000\hspace{0.2em}1010\]

Helpotetaan kirjoittamista muuntamalla luku heksadesimaalimuotoon:

\[0\text{x}\hspace{0.2em}80\hspace{0.2em}00\hspace{0.2em}01\hspace{0.2em}0\text{A}\]

Seuraavaksi on huomioitava tallennuksessa käytetty tavujärjestys:

Big Endian: Jos tavujärjestys on Big Endian, luvun tallennusmuoto on luonnollinen, eli juuri tuo minkä juuri saimme:

\[0\text{x}\hspace{0.2em}80\hspace{0.2em}00\hspace{0.2em}01\hspace{0.2em}0\text{A}\]

Little Endian: Jos tavujärjestys on Little Endian, käännetään tavujen järjestys ja saadaan:

\[0\text{x}\hspace{0.2em}0\text{A}\hspace{0.2em}01\hspace{0.2em}00\hspace{0.2em}80\]
Huomaa, että kyse on tavujärjestyksestä, eikä puolitavujen tai bittien järjestystä. Little Endianissa on siis tavut käänteisessä järjestyksessä. Siten Big Endian -muodossa oleva luku 0x8000010A on Little Endian -muodossa 0x0A010080 eikä esimerkiksi 0xA0100008!

Etumerkillisen luvun muuntaminen kymmenjärjestelmän luvuksi

Esimerkki: Etumerkillisen 32-bittisen Big Endian -muodossa olevan luvun 0x8000002C muuntaminen kymmenjärjestelmän luvuksi

Luku 0x8000002C on Big Endian -tavujärjestyksessä, joten tavujärjestys on valmiiksi luonnollinen, eikä sitä tarvitse kääntää.

Muunnetaan luku binääriluvuksi, jotta näemme helpommin, mikä luvun merkki on:

\[\begin{align*} 0\text{x}\hspace{0.2em}80\hspace{0.2em}00\hspace{0.2em}00\hspace{0.2em}2\text{C} \\ = 0\text{b}\hspace{0.2em}1000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0010\hspace{0.2em}1100 \end{align*}\]

Huomataan, että eniten merkitsevä bitti on 1, joten luku on negatiivinen. Muunnetaan luvun loppuosa eli itseisarvoa ilmaiseva osa kymmenjärjestelmän luvuksi:

\[\begin{align*} 0\text{b}\hspace{0.2em}000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0010\hspace{0.2em}1100 \\ = 32+8+4 \\ = 44 \end{align*}\]

Saatiin luku \(44\). Koska luku on negatiivinen, lisätään merkki ja saadaan

\(-44\).

Etuja ja haittoja

  • Etumerkillinen esitysmuoto on ihmiselle helppo ymmärtää, koska siinä näkee suoraan etumerkistä, onko luku positiivinen vai negatiivinen ja lopuista biteistä näkee suoraan luvun itseisarvon.

  • Laskentapiirissä kuitenkin tarvitaan ylimääräistä logiikkaa huomioimaan merkin vaikutus. Etumerkillistä esitysmuotoa ei juuri käytetä missään, koska on tärkeämpää tehostaa tietokoneen laskentaa kuin näyttää luvun tallennustapa ihmiselle. Lukuhan kuitenkin näytetään ihmiselle sopivassa muodossa esimerkiksi tietokoneen näytöllä.

Yhden komplementti

Yhden komplementti -muodossa ei-negatiiviset luvut esitetään halutun pituisena normaalina binäärilukuna. Negatiiviset luvut ilmaistaan kääntämällä luvun jokainen bitti (0 ➝ 1 ja 1 ➝ 0). Tässäkin esitysmuodossa näkee eniten merkitsevästä bitistä, onko luku positiivinen vai negatiivinen.

Kymmenjärjestelmän luvun muuntaminen yhden komplementti -muotoon

Esimerkki: 32-bittinen yhden komplementti -muoto luvusta -266

Muutetaan ensin luvun -266 itseisarvo 266 binääriluvuksi ja saadaan:

\[0\text{b}\hspace{0.2em}1\hspace{0.2em}0000\hspace{0.2em}1010\]

Ilmaistaan luku halutussa pituudessa eli 32-bittisenä ja saadaan:

\[0\text{b}\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0001\hspace{0.2em}0000\hspace{0.2em}1010\]

Käännetään bitit ja saadaan:

\[0\text{b}\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1110\hspace{0.2em}1111\hspace{0.2em}0101\]

Helpotetaan kirjoittamista muuntamalla luku heksadesimaalimuotoon:

\[0\text{x}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FE}\hspace{0.2em}\text{F}5\]

Seuraavaksi on huomioitava tallennuksessa käytetty tavujärjestys: Big Endian: Jos tavujärjestys on Big Endian, luvun tallennusmuoto on luonnollinen, eli juuri tuo minkä juuri saimme:

\[0\text{x}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FE}\hspace{0.2em}\text{F}5\]

Little Endian: Jos tavujärjestys on Little Endian, käännetään tavujen järjestys ja saadaan:

\[0\text{x}\hspace{0.2em}\text{F5}\hspace{0.2em}\text{FE}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\]

Yhden komplementtimuodossa olevan luvun muuntaminen kymmenjärjestelmän luvuksi

Yhden komplementtimuodossa oleva luku muunnetaan kymmenjärjestelmän luvuksi seuraavasti:

  1. Muutetaan tavujärjestys Big Endian -muotoon, ellei se jo ole siinä muodossa.
  2. Jos eniten merkitsevä bitti on 0, luku on ei-negatiivinen ja luvun voi muuntaa suoraan kymmenjärjestelmän luvuksi.
  3. Jos eniten merkitsevä bitti on 1, luku on negatiivinen. On siis otettava sen vastaluku kääntämällä. Sen jälkeen muunnetaan luku kymmenjärjestelmän luvuksi ja lisätään eteen merkiksi \(-\).

Esimerkki: 32-bittisen Little Endian yhden komplementti -muodossa olevan luvun 0x00FFFFFF muuntaminen kymmenjärjestelmän luvuksi

Luku 0x00FFFFFF on Little Endian -tavujärjestyksessä, joten tavujärjestys on käännettävä ensin tavalliseen Big Endian -muotoon ja saadaan:

\[0\text{x}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}00\]

Muunnetaan luku binääriluvuksi, jotta näemme helpommin, mikä luvun merkki on:

\[\begin{align*} 0\text{x}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}00 \\ = 0\text{b}\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}0000\hspace{0.2em}0000 \end{align*}\]

Huomataan, että eniten merkitsevä bitti on 1, joten luku on negatiivinen. Koska luku on negatiivinen, otetaan nyt sen vastaluku kääntämällä bitit (0 ➝ 1 ja 1 ➝ 0) ja saadaan:

\[0\text{b}\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}1111\hspace{0.2em}1111\]

Muunnetaan näin saatu binääriluku kymmenjärjestelmän luvuksi:

\[\begin{align*} 0\text{b}\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}1111\hspace{0.2em}1111 \\ = 128+64+32+16+8+4+2+1 \\ = 255 \end{align*}\]

Saatiin luku \(255\).

Koska luku on negatiivinen, lisätään merkki ja saadaan

\(-255\).

Etuja ja haittoja

  • Yhden komplementti on helppo toteuttaa laitteistolla. Bittien kääntämisen piiri on helppo toteuttaa ja vaihto toteutuu hetkessä.
  • Haittana on myös, että nollalla on kaksi esitysmuotoa, -0 ja + 0.
  • Lisäksi haittana on se, että laskenta monimutkaistuu. Tässä esitysmuodossa positiivisen ja negatiivisen luvun yhteenlasku ei voi koskaan aiheuttaa varsinaista kokonaisluvun ylivuotovirhettä, mutta laskettaessa yhteen positiivinen ja negatiivinen luku, joiden summan pitäisi olla positiivinen, joudutaan lisäämään ylivuotobitti tulokseen. Esimerkki:
Yhden komplementin yhteenlasku -1 + 3
   1111 1111 1111 1111 1111 1111 1111 1110     Luku -1
+  0000 0000 0000 0000 0000 0000 0000 0011     Luku 3
==========================================
 1 0000 0000 0000 0000 0000 0000 0000 0001     Summa 1, ylivuotobitti 1
+  0000 0000 0000 0000 0000 0000 0000 0001     Lisätään ylivuotobitti
==========================================
   0000 0000 0000 0000 0000 0000 0000 0010     Saadaan summaksi 2.

Ongelma ei ehkä vaikuta kovin suurelta, mutta se silti hidastaa laskentaa. Niinpä yleisemmin käytetään kahden komplementti -esitysmuotoa, jossa jo tiedon tallennusvaiheessa on huomioitu yhdellä lisäys, joten sitä ei tarvitse enää tehdä laskentaa suorittaessa.

Kahden komplementti

Kahden komplementti on tyypillisin tapa tallettaa kokonaislukuja tietokoneella. Tätä esitysmuotoa käytetään myös ttk-91 esimerkkikoneessa.

Kymmenjärjestelmän luvun muuntaminen kahden komplementti -muotoon

Kahden komplementti -muodossa ei-negatiiviset luvut esitetään halutun pituisena normaalina binäärilukuna. Negatiiviset luvut ilmaistaan kääntämällä luvun itseisarvon mukainen halutunpituisen binääriluvun jokainen bitti (0 ➝ 1 ja 1 ➝ 0) ja lisäämällä luku 1. Tässäkin esitysmuodossa näkee eniten merkitsevästä bitistä, onko luku positiivinen vai negatiivinen.

Esimerkki: 32-bittinen kahden komplementti -muoto luvusta -266

Muutetaan ensin luvun -266 itseisarvo 266 binääriluvuksi ja saadaan:

\[0\text{b}\hspace{0.2em}1\hspace{0.2em}0000\hspace{0.2em}1010\]

Ilmaistaan luku halutussa pituudessa eli 32-bittisenä ja saadaan:

\[0\text{b}\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0001\hspace{0.2em}0000\hspace{0.2em}1010\]

Käännetään bitit ja saadaan:

\[0\text{b}\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1110\hspace{0.2em}1111\hspace{0.2em}0101\]

Lisätään 1 ja saadaan:

\[0\text{b}\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1110\hspace{0.2em}1111\hspace{0.2em}0110\]

Helpotetaan kirjoittamista muuntamalla luku heksadesimaalimuotoon:

\[0\text{x}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FE}\hspace{0.2em}\text{F}6\]

Seuraavaksi on huomioitava tallennuksessa käytetty tavujärjestys:

Big Endian: Jos tavujärjestys on Big Endian, luvun tallennusmuoto on luonnollinen, eli juuri tuo minkä juuri saimme:

\[0\text{x}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FE}\hspace{0.2em}\text{F}6\]

Little Endian: Jos tavujärjestys on Little Endian, käännetään tavujen järjestys ja saadaan:

\[0\text{x}\hspace{0.2em}\text{F6}\hspace{0.2em}\text{FE}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\]

Kahden komplementti -muodossa olevan luvun muuntaminen kymmenjärjestelmän luvuksi

Kahden komplementtimuodossa oleva luku muunnetaan kymmenjärjestelmän luvuksi seuraavasti:

  1. Muutetaan tavujärjestys Big Endian -muotoon, ellei se jo ole siinä muodossa.
  2. Jos eniten merkitsevä bitti on 0, luku on ei-negatiivinen ja luvun voi muuntaa suoraan kymmenjärjestelmän luvuksi.
  3. Jos eniten merkitsevä bitti on 1, luku on negatiivinen. Silloin käännetään bitit ja lisätään 1. Sen jälkeen muunnetaan luku kymmenjärjestelmän luvuksi ja lisätään eteen merkiksi \(-\).

Esimerkki: 32-bittisen Little Endian kahden komplementti -muodossa olevan luvun 0x00FFFFFF muuntaminen kymmenjärjestelmän luvuksi

Luku 0x00FFFFFF on Little Endian -tavujärjestyksessä, joten tavujärjestys on käännettävä ensin tavalliseen Big Endian -muotoon ja saadaan:

\[0\text{x}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}00\]

Muunnetaan luku binääriluvuksi, jotta näemme helpommin, mikä luvun merkki on:

\[\begin{align*} 0\text{x}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}\text{FF}\hspace{0.2em}00 \\ = 0\text{b}\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}1111\hspace{0.2em}0000\hspace{0.2em}0000 \end{align*}\]

Huomataan, että eniten merkitsevä bitti on 1, joten luku on negatiivinen. Jos eniten merkitsevä bitti olisi 0, luku olisi positiivinen eikä siitä tarvitsisi ottaa vastalukua itseisarvon selvittämiseksi. Koska luku tässä tapauksessa kuitenkin on negatiivinen, otetaan sen vastaluku.

Käännetään ensin bitit (0 ➝ 1 ja 1 ➝ 0) ja saadaan:

\[0\text{b}\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}1111\hspace{0.2em}1111\]

Koska käytössä on kahden komplementin esitysmuoto, lisätään vielä tähän 1 ja saadaan vastaluku selville:

\[0\text{b}\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0001\hspace{0.2em}0000\hspace{0.2em}0000\]

Muunnetaan näin saatu binääriluku kymmenjärjestelmän luvuksi:

\[\begin{align*} 0\text{b}\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0000\hspace{0.2em}0001\hspace{0.2em}0000\hspace{0.2em}0000 \\ = 256 \end{align*}\]

Saatiin luku \(256\).

Koska luku on negatiivinen, lisätään merkki ja saadaan

\(-256\).

Etuja ja haittoja

  • Laskenta tehostuu, sillä bittien kääntämisen lisäksi luku 1 lisätään vain lukua tallentaessa ja sen jälkeen laskenta tapahtuu aina ilman ylimääräistä ykkösen lisäystä, toisin kuin yhden komplementissa.
  • Vähennyslasku voidaan toteuttaa yhteenlaskupiirin avulla muuttamalla ennen yhteenlaskua vähennettävä luku vastaluvukseen ottamalla siitä kahden komplementti.
  • Etuna on se, että nollallla on vain yksi esitysmuoto.
  • Haittana on se, että pienimmällä luvulla ei ole vastalukua. Siis esimerkiksi 8-bittinen kahden komplementti -muodossa tallennettava luku -128 = 0b10000000 on pienin 8 bitillä ilmaistava luku. Jos siitä otetaan vastaluku, saadaan 0b01111111 + 1 = 0b10000000. Luvulla 128 olisi siis sama ilmaisumuoto kuin luvulla -128. Niinpä positiivisia lukuja on aina yksi vähemmän kuin negatiivisia lukuja. Tämä huomioidaan automaattisesti laitteistossa. Vähennyslaskun toteutuksessa tämä ei ole ongelma. Esimerkiksi vähennyslasku 127 - 128 voidaan silti toteuttaa yhteenlaskupiirin avulla muodossa 127 + -(-128) eli 0b01111111 + 0b10000000 = 0b11111111 eli -1 kuten pitääkin olla.

Esimerkki kahden komplementin yhteenlaskusta

Kahden komplementin yhteenlasku -1 + 3
   1111 1111 1111 1111 1111 1111 1111 1111     Luku -1
+  0000 0000 0000 0000 0000 0000 0000 0011     Luku 3
==========================================
 1 0000 0000 0000 0000 0000 0000 0000 0010     Summa 2, ylivuotobitti 1

Ylivuotobitti jätetään aina huomioimatta.

Kahden komplementtimuodossa olevien lukujen yhteenlaskun suorittavaa laskentapiiriä suunniteltaessa huomioidaan ylivuodot seuraavasti:

  • Varsinainen ylivuotobitti eli esimerkiksi 32 bitin pituisten lukujen yhteenlaskussa ylimääräinen 33. bitti jätetään huomioimatta. Se ei siis ilmaise ylivuotoa.
  • Negatiivisen ja ei-negatiivisen luvun yhteenlaskun tulos ei koskaan aiheuta kokonaisluvun ylivuotovirhettä.
  • Samanmerkkisten lukujen yhteenlaskussa kokonaisluvun ylivuoto havaitaan, jos yhteenlaskun tuloksessa merkki on muuttunut. Positiivisten lukujen summanhan pitää olla positiivinen ja negatiivisten lukujen summan negatiivinen. Virhe siis havaitaan, jos summassa eniten merkitsevä bitti on eri kuin yhteenlaskettavien lukujen eniten merkitsevät bitit.

Vakiolisäys

Vakiolisäys on yllä mainituista tavoista poikkeava tapa ilmaista kokonaislukuja. Siinä on määritelty jokin vakio, joka lisätään lukuihin, jotta niistä saadaan tiedon tallettamiseen soveltuva ei-negatiivinen kokonaisluku.

Usein vakio on valittu suunnilleen mahdollisen lukujoukon keskeltä, jolloin saadaan lähes sama määrä negatiivisia ja ei-negatiivisia lukuja. Esimerkiksi tavulla eli 8 bitillä voidaan tallettaa luvut muodossa 0b00000000 … 0b11111111 eli 0…255 ja käyttämällä vakiota 127 saadaan ilmaistua luvut -127…128.

Kymmenjärjestelmän luvun muuntaminen vakiolisäys 127 -muotoon

Esimerkki: Luvun \(-50\) muuntaminen vakiolisäys 127 -muotoon:

Lisätään ensin lukuun \(-50\) luku \(127\):

\[-50 + 127 = 77\]

Muunnetaan näin saatu luku \(77\) binääriluvuksi ja saadaan:

\[0\text{b}\hspace{0.2em} 0100\hspace{0.2em} 1101\]

Esimerkki: Luku \(7\) vakiolisäys 127 -muodossa:

Lisätään ensin lukuun \(7\) luku \(127\):

\[7 + 127 = 134\]

Muunnetaan näin saatu luku \(134\) binääriluvuksi ja saadaan:

\[0\text{b}\hspace{0.2em} 1000\hspace{0.2em} 0110\]

Vakiolisäys 127 -muodossa olevan luvun muuntaminen tavalliseksi luvuksi

Esimerkki: Vakiolisäys 127 -muodossa olevan luvun \(0\text{b}\hspace{0.2em} 1100\hspace{0.2em} 0001\) muuntaminen kymmenjärjestelmän luvuksi.

Binääriluku \(0\text{b}\hspace{0.2em} 1100\hspace{0.2em} 0001\) on kymmenjärjestelmässä \(128+64+1=193\)

Vähennetään luvusta \(193\) vakio \(127\):

\[193 - 127 = 66\]

Niinpä tuo luku on kymmenjärjestelmän lukuna \(66\).

Liukuluvut

Tietokoneen suorittimessa on yleensä kokonaislukujen laskemiseen tarkoitetun yksikön lisäksi liukulukujen laskentaa hoitava yksikkö. Liukulukuina voi esittää rajallisella tarkkuudella rationaalilukuja. Tyypillisesti liukulukulaskentayksiköt suorittavat laskennan olettaen, että liukuluvut ovat IEEE-754 standardin mukaisessa muodossa. Käsitellään tässä osiossa kyseisen standardin 32-bittisiä liukulukuja.

IEEE-754 standardin 32-bittisen liukuluvun kentät:

  • 1 bitti merkkiä varten (0 = + ja 1 = -)
  • 8 bittiä eksponenttikenttä
  • 23 bittiä mantissakenttä

Tarkastellaan esimerkin avulla, miten desimaaliluku muunnetaan liukuluvuksi.

Desimaaliluvun muuntaminen IEEE-liukuluvuksi

Muunnetaan desimaaliluku \(-50,8125\) liukuluvuksi vaiheittain seuraavasti.

1. Merkki (+ vai -)

IEEE-liukuluvussa ensimmäinen bitti vasemmalta ilmaisee merkin. Positiivista lukua ilmaisee luku 0, sillä (-1)0 = 1. Negatiivista lukua ilmaisee luku 1, sillä (-1)1 = -1.

Positiivisen luvun (+) merkki on 0
Negatiivisen luvun (-) merkki on 1

Luku \(-50,8125\) on negatiivinen, joten etumerkiksi tulee 1. Ensimmäinen osa liukulukuesitystä on selvitetty.

Liukulukutaulukko

2. Desimaaliluvun kokonaislukuosan muunto binääriluvuksi

Muunnetaan desimaaliluvun \(50,8125\) kokonaislukuosa \(50\) binääriluvuksi jakoyhtälöä soveltaen.

Jakolasku Osamäärä Jakojäännös Jakoyhtälö
50 : 2 25 \(0\) \(50=2\cdot 25+\mathbf{0}\)
25 : 2 12 \(1\) \(25=2\cdot 12+\mathbf{1}\)
12 : 2 6 \(0\) \(12=2\cdot 6+\mathbf{0}\)
6 : 2 3 \(0\) \(6=2\cdot 3+\mathbf{0}\)
3 : 2 1 \(1\) \(3=2\cdot 1+\mathbf{1}\)
1 : 2 0 \(1\) \(1=2\cdot 0+\mathbf{1}\)

Saimme lopulta osamääräksi 0, joten lukumuunnos on valmis. Nyt haluttu binääriluku saadaan valitsemalla jakojäännökset alhaalta ylös luettuna.

Siten \(50 = 0\text{b}110010\).

3. Desimaaliluvun desimaaliosan muunto binääriluvun binääriosaksi

Muunnetaan desimaaliluvun \(50,8125\) desimaaliosa \(8125\) (eli pilkun oikealla puolella oleva osa) binääriosaksi seuraavanlaisella algoritmilla.

Kertolasku Tulos Kokonaisosa = bitti
\(0,8125\cdot 2\) \(1,\color{blue}\mathbf{625}\) \(1\)
\(0,{\color{blue}\mathbf{625}}\cdot 2\) \(1,\color{green}\mathbf{250}\) \(1\)
\(0,{\color{green}\mathbf{250}}\cdot 2\) \(0,\color{blue}\mathbf{500}\) \(0\)
\(0,{\color{blue}\mathbf{500}}\cdot 2\) \(1,\mathbf{000}\) \(1\)

Saimme lopulta alimmalla rivillä kertolaskun tuloksen desimaaliosaksi 0, joten binääriosan laskeminen on valmis. Nyt haluttu binääriosa on sarakkeessa Tulos olevat kokonaisosat ylhäältä alas luettuna. Kokonaisosat näkyvät myös sarakkeessa Kokonaisosa = bitti.

Siten desimaaliosa luvusta \(0,8125\) on \(0\text{b}\hspace{0.2em}1101\).

Tässä tapauksessa saimme äärellisen pituisen binääriosan. Jos desimaaliosa olisi esimerkiksi luvusta 1,20, niin binääriosa olisi päättymätön. Tällöin binääriosa huomioidaan vain siihen saakka mitä on mahdollista saada mahtumaan liukulukuun ja loput jätetään huomioimatta. Käytössä on myös tietyt pyöristyssäännöt, jotka eivät kuitenkaan kuulu tämän kurssin oppimistavoitteisiin. IEEE:n 32-bittisissä liukuluvuissa on mahdollista sisällyttää enintään 24 bitin tarkkuus.

4. Eksponentin valinta

Yhdistetään kokonaisosa ja binääriosa

Yhdistetään kohdassa 2 muunnettu kokonaisosa \(\mathbf{110010}\) ja binääriosa \(\mathbf{1101}\).

Saadaan \(\mathbf{110010.1101}\).

Seuraavaksi näin saatu binääriosan sisältävä binääriluku normalisoidaan, eli binääripiste siirretään eniten merkitsevän ykkösbitin oikealle puolelle.

Lasketaan eksponentti

Kymmenjärjestelmässä voidaan ilmaista lukuja seuraavasti:

\(1982 = 1,982 \cdot 10^3\).

Pilkkua siirrettiin vasemmalle 3 numeron verran, joten luku pitää kertoa tuhannella eli luvulla \(10^3\).

Liukuluvun normalisoinnissa tehdään vastaavanlainen toimitus.

Eksponentti saadaan laskemalla kuinka paljon binääripistettä tulee siirtää jotta binääriesityksen ensimmäisen 1-bitin oikealle puolelle tulee piste. Koska meillä on luku \(110010.1101\), on siirrettävä pistettä 5 bittiä vasemmalle. Binääripisteen vasemmalle siirtäminen yhden bitin verran vastaa kahdella jakamista, kahden bitin verran siirtäminen kaksi kertaa kahdella jakamista ja niin edelleen. Niinpä tämä jakaminen on huomioitava seuraavasti:

\[0\text{b}\hspace{0.2em}110010.1101 = 0\text{b}\hspace{0.2em}1.100101101 \cdot 2^5\]

Pistettä siirrettiin 5 pistettä vasemmalle jolloin saimme luvun muotoon

\(0\text{b}\hspace{0.2em}1.100101101 \cdot 2^5\).

Eksponentti on siis 5.

Samalla saatiin määriteltyä mantissa 1.100101101. Palataan siihen kuitenkin alempana.

Muunnetaan eksponentti vakiolisäys 127 -muotoon

Tämä tapahtuu helposti. Eksponenttiin 5 lisätään luku 127, jolloin saadaan luku 132.

Muunnetaan vakiolisäys 127 -muodossa oleva eksponentti binääriluvuksi

Muunnetaan saatu luku 132 binääriluvuksi, tällä kertaa muunnostaulukon avulla. 132 on kahden potenssien summana 128+4, joten saamme täytettyä taulukon seuraavasti:

Tavutaulukko

Niinpä \(132 = 0\text{b}\hspace{0.2em}1000\hspace{0.2em}0100\).

Olemme saaneet toisen osan eli eksponenttikentän liukulukuesitystä varten.

Liukulukutaulukko

5. Mantissakenttä

Tämä on myös nyt helppoa. Yllä eksponenttia selvittäessä on jo muunnettu binääriesitys \(110010.1101\) muotoon \(1.100101101 \cdot 2^5\).

Koska IEEE-liukulukustandardissa käytetään piilobittiä, eli mantissan ensimmäinen osa oletetaan luvuksi 1, sitä ei tarvitse merkitä mantissakenttään. Binääriesitys on muodossa 1.100101101. Pisteen vasemmalla puolella olevaa bittiä 1 ei siis laiteta mantissakenttään.

Piilobitti on IEEE-liukuluvussa binääripisteen vasemmalla puolella oleva bitti 1, jonka oletetaan olevan 1, joten sitä ei tarvitse merkitä. Piilobitin ansiosta saadaan ilmaistua luku tarkemmin.

Mantissakentän pituus on 23 bittiä. Se alkaa tuosta binääripisteen oikealta puolen ja loppuosa täytetään nollilla. Mantissakenttä on siten tässä tapauksessa \(10010110100000000000000\).

Liukulukutaulukko

Joten luku -50,8125 on IEEE 32-bittisenä liukulukuna

\(1100 0010 0100 1011 0100 0000 0000 0000\).

Kannattaa harjoitella näitä muutoksia eri luvuilla. Muunna esimerkiksi nämä luvut 2,5 ja 4,125 IEEE 32-bittiseksi liukuluvuksi. On myös mielenkiintoista muuntaa liukuluvuksi luku jonka desimaaliosa ei ole täsmälleen kahden potenssien summa, esimerkiksi luku 5,1.

1-ulotteinen taulukko

Tietokoneen muistissa tietoon viitataan tavuosoitteilla tai sanaosoitteilla. Osoitteet ovat perättäisiä kokonaislukuja. Yksiulotteinen taulukko talletetaan peräkkäisiin muistipaikkoihin.

Taulukon kokoa ei välttämättä talleteta mihinkään. Sen sijaan ohjelmoija tietää taulukon koon. Esimerkiksi ttk-91 symbolisella konekielellä globaali (koko ohjelman “näkemä”) 20-alkioinen taulukko, jonka nimi (symboli) on A, määritellään seuraavasti:

; ttk-91
A DS 20

Taulukon alkio voidaan sitten ladata rekisteriin tällä tavalla:

; ttk-91
; Oletetaan, että taulukko A alkaa osoitteesta 100

load r1, =5      ; halutun alkion indeksi 5 rekisteriin r1
load r2, A(r1)   ; rekisteriin r2 arvo osoitteesta A + R1 eli 100 + 5 = 105

Globaalit symbolit ovat koko ohjelman “näkemiä” symboleita. Niitä voi siis käyttää missä tahansa kohdassa ohjelmaa. Ohjelman laajetessa voi olla hyvä pitää jossakin yllä eri taulukoiden kokoa. Sen voi tehdä esimerkiksi luomalla kutakin taulukkoa varten erillisen symbolin, jonka alussa on sana koko ja lopussa taulukon nimi. Esimerkiksi näin:

; ttk-91
A DS 20
kokoA EQU 20

Näin taulukkoa käsitellessä voi ohjelmakoodissa käyttää tuota kokoA -symbolia taulukon koon selvittämiseen.

2-ulotteinen taulukko eli matriisi

2-ulotteisessa taulukossa eli matriisissa on tietty määrä rivejä ja sarakkeita, vaikkapa 5 riviä ja 4 saraketta. Tietokoneessa on kuitenkin vain yksiulotteisia muistipaikkoja. Niinpä useampiulotteiset taulukot talletetaan 1-ulotteisina taulukkoina, mutta ennen taulukon tietyn rivin tietystä sarakkeesta lukemista tai siihen tallentamista muutetaan ohjelmallisesti rivi ja sarake vastaamaan 1-ulotteisen taulukon indeksiä.

2-ulotteisia taulukoita käsitellään riveittäin tai sarakkeittain tallennettuna.

Riveittäin tallennettu taulukko

Jos arvioit, että tulet käyttämään 2-ulotteista taulukkoa pääosin rivijärjestyksessä, kannattaa tallentaa tiedot riveittäin.

Matriisi riveittäin

Kuvan mukaisessa taulukossa on 5 riviä (indeksin 0..4) ja 4 saraketta (indeksin 0..3). Kuvassa on havainnollistamisen vuoksi taulukon alkioina ne 1-ulotteisen taulukon indeksit, joihin kullakin rivillä ja sarakkeessa oleva tieto tallennetaan.

Nuo luvut ovat muistissa perättäisissä muistipaikoissa järjestyksessä 0, 1, 2, 3, 4, 5, …, 19.

Kaava indeksin laskemiseen, kun tallennustapana on riveittäin tallennettu taulukko:

\[\begin{align*} \text{indeksi} = \text{rivi} \cdot \text{leveys} + \text{sarake} \end{align*}\]

Kaavassa leveys tarkoittaa sarakkeiden määrää taulukossa.

Esimerkki: Kuvan mukaisen taulukon alkion [1,2] eli rivillä 1 sarakkeessa 2 olevan alkion indeksi on \(1 \cdot 4 + 2 = 6.\)

Ttk-91:ssä indeksin laskeminen ja sen käyttäminen taulukkoon tallentamiseen suoritettaisiin seuraavasti. Määritellään ensin 2-ulotteinen taulukko C, jossa on 5 riviä ja 4 saraketta määrittelemällä 5*4=20 suuruinen taulukko. Sitten lasketaan alkion C[1,2] indeksi ja tallennetaan siihen arvo 100.

; ttk-91
C DS 20           ; Tilanvaraus taulukolle C
leveysC EQU 4     ; sarakkeiden määrä 4

load r2, =1       ; rivi 1, joten luku 1 rekisteriin R2
mul r2, =leveysC  ; kerrotaan tuo rivi leveydellä
add r2, =2        ; lisätään haluttu sarake eli luku 2
; Nyt rekisterissä R2 on haluttu indeksi eli luku 6

load r1, =100     ; r1 <- luku 100
; Talletetaan r1:n arvo 100 taulukon C indeksiin 6
store r1, C(r2)   ; nyt C[1,2] = 100  

svc sp, =halt     ; Ohjelman lopetus

Sarakkeittain tallennettu taulukko

Jos arvioit, että tulet käyttämään 2-ulotteista taulukkoa pääosin sarakejärjestyksessä, kannattaa tallentaa tiedot sarakkeittain.

Matriisi sarakkeittain

Kuva on samankokoisesta taulukosta kuin yllä, eli siinä on 5 riviä (indeksin 0..4) ja 4 saraketta (indeksin 0..3). Myös tässä kuvassa on taulukon alkioina ne 1-ulotteisen taulukon indeksit, joihin kullakin rivillä ja sarakkeessa oleva tieto tallennetaan.

Nuo luvut ovat muistissa perättäisissä muistipaikoissa järjestyksessä 0, 1, 2, 3, 4, 5, …, 19. Ensimmäisenä on siis tallennettu sarakkeen 0 sisältö, sen jälkeen sarakkeen 2 sisältö ja niin edelleen.

Kaava indeksin laskemiseen, kun tallennustapana on sarakkeittain tallennettu taulukko:

\[\begin{align*}\text{indeksi} = \text{sarake} \cdot \text{korkeus} + \text{rivi} \end{align*}\]

Kaavassa korkeus tarkoittaa rivien määrää taulukossa.

Esimerkki: Kuvan mukaisen taulukon alkion [3,1] eli rivillä 3 sarakkeessa 1 olevan alkion indeksi on \(1 \cdot 5 + 3 = 8.\)


Copyright © 2021 Harri Kähkönen. (Github: epicharri)