Det binära talsystemet Datatyper i programspråk

Det binära talsystemet
Datorer arbetar med digitala signaler, signaler som bara kan anta två olika värden. Dessa representeras elektriskt som spänning eller frånvaro av spänning, och beteckas 1 och 0.
Trots att datorer alltså egentligen bara kan räkna till ett så kan man representera godtyckligt stora tal genom att använda det binära talsystemet. Vårt vanliga talsystem är ett så kallat positionssystem med den för människor praktiska basen 10. Vi har bara siffor från 0 till 9, större tal
representeras med flersiffriga tal där siffrorna successivt får större vikt ju längre åt vänster de
står i talet. Detta kallas decimalsystemet. Decimalsystemet och även våra moderna siffror är en
arabisk uppfinning, men själva namnet kommer från grekiskans och latinets ord för “tio
gånger”.
3
2
1
0
4701 = 4 ⋅ 10 + 7 ⋅ 10 + 0 ⋅ 10 + 1 ⋅ 10 = 4000 + 700 + 0 + 1
Om man bara har siffrorna 0 och 1 kan man i stället använda basen 2. Tal representerade i bas
2 noteras ofta med ett index om det finns risk för förväxling.
3
2
1
0
1101 2 = 1 ⋅ 2 + 1 ⋅ 2 + 0 ⋅ 2 + 1 ⋅ 2 = 8 + 4 + 0 + 1 = 13
En binär siffra kallas ofta en bit, vilket från början var en hopdragning av engelskans “binary
n
digit”. Med n binära siffror kan man representera 2 olika tal. Med 8 bitar kan man representera
256 olika värden, med 16 bitar 65536, och med 32 bitar litet drygt 4 miljarder.
Datatyper i programspråk
Datorer lagrar och behandlar alla data som binära bitar, oftast i grupper om 8 bitar som kallas
bytes1. En dators minne är oftast organiserat i bytes, så att man kan adressera enstaka grupper
om 8 bitar för att läsa och skriva dem.
Heltal
För att representera heltal används en eller flera bytes. Den vanligaste sortens heltal representeras med 32 bitar, lagrade i 4 bytes efter varandra i minnet. Det finns i de flesta programspråk
även heltalstyper med 16 och 8 bitar. Om man vill representera positiva heltal med 8 bitar ser
det ut så här:
0
1
43
254
255
00000000
00000001
00101011
11111110
11111111
När man representerar tal med mer än en enstaka byte stöter man på problemet i vilken ordning
man skall lägga dem i minnet. Tyvärr har inte datortillverkarna lyckats enas om en standard, så
en PC lägger dem i en ordning, och en Mac i motsatt ordning. Man talar om “little-endian” och
“big-endian” representation. Detta är en källa till förtret för programmerare som vill få sina program att fungera på flera plattformar.
Om man även vill ha negativa tal (och det vill man oftast) så kan man låta hälften av talen representera negativa tal, och man väljer då oftast de största, de som har en etta längst till vänster.
1. Ordet “byte” är en lätt förvanskning av engelskans “bite” som betyder “tugga”, alltså en lagom mängd
bitar att behandla i taget. Stavningen ändrades från “i” till “y” för att undvika förväxling med det
liknande ordet “bit”.
Detta kallas tvåkomplementrepresentation. Exakt varför det heter så skulle vara litet för krångligt att förklara här, men representationen gör att man kan använda samma regler för addition
med negativa tal som med positiva tal. Den som vill veta detaljerna hänvisas till det inledande
kapitlet i valfri bok i datorteknik. Några exempel på 8-bitars tal i tvåkomplementsrepresentation
finner du nedan.
0
1
43
127
00000000
00000001
00101011
01111111
-1
-2
-43
-128
11111111
11111110
11010101
10000000
Tal med decimaler
Heltal är bra till mycket, men alla former av beräkningar behöver tal med högre noggrannhet,
dvs med decimaler. Ett sätt är att använda några av bitarna i talet som vikter för negativa potenser av 2. Detta kallas fixtal och är ett ganska vanligt sätt att göra enklare beräkningar där precisionen inte är huvudsaken, exempelvis i grafikkort och i digitala filter för signalbehandling.
Generella datorer använder dock oftast vad som kallas flyttal (floating point numbers). Det är
egentligen precis samma system som man använder för teknik och fysik för att representera
väldigt stora eller väldigt små tal med en tillräcklig precision utan att skriva långa rader med
nollor: man använder en notation med en mantissa och en exponent:
14
362842970000000 = 3.6284297 ⋅ 10
Mantissan “3.6284297” anger värdesiffrorna, medan exponenten “14” anger hur många steg
man har flyttat decimalkommat åt ena eller andra hållet. Datorer gör likadant, men binärt:
11
1011000000002 = 1.011 2 ⋅ 2 , 0.00001101 2 = 1.1012 ⋅ 2
–5
I moderna datorer används två sorters flyttal, en version har 64 bitar (8 bytes) varav 53 bitar används för mantissan och 11 bitar för exponenten. Det är inte alltid man behöver så hög precision
eller så stort talområde, och därför används ofta en variant med 32 bitar (4 bytes), med 24 bitar
för mantissan och 8 bitar för exponenten. Representationen med 32 bitar tar hälften så stor plats
i minnet, samt går snabbare att överföra och behandla. I många programmeringsspråk har dessa
datatyper egna namn. Oftast heter 32-bitarsversionen “float” och 64-bitarsversionen “double”.
Text
Datorer behandlar som bekant inte bara siffror. En stor del av de data som lagras och bearbetas
av datorer är text. Text lagras som en följd av tecken, där varje tecken representeras (kodas) som
ett heltal. Kodningen som används kallas för teckentabell och varierar beroende på vilket språk
texten använder, och tyvärr också beroende på vilket slags dator som används. En vanlig teckentabell som används i många sammanhang och fungerar för de flesta västerländska språk är
den som kallas “ISO 8859-Latin-1”, och den använder 8 bitar för varje tecken. För språk med
många speciella accenttecken krävs dock olika varianter av teckentabellen, och de latinska,
grekiska och kyrilliska alfabeten får tyvärr inte plats samtidigt bland de 256 tecknen som kan
representeras. En bra och generell internationell standard som löser detta problem är Unicode
(www.unicode.org), men det kommer nog tyvärr att ta lång tid innan den standarden är genomförd överallt.
Så mycket kan man i alla fall säga att text representeras i datorer som en följd av tecken, där
varje tecken kodas som ett binärt tal enligt en överenskommen teckentabell. I teckentabellen ingår mellanslag, skiljetecken, siffror och ett antal specialtecken, och även vissa formateringstecken som radslut har egna koder.
Datorteknik för programmerare
Sett ur traditionell ingenjörsvetenskaplig synvinkel så bygger datorteknik direkt på digitalteknik, och på många tekniska utbildningar ges stora separata kurser i digitalteknik, datorteknik
och digital konstruktion med mikrodatorer. På andra utbildningar läser man endast programmering och mycket litet eller inget alls om tekniken bakom datorers funktion. Datorteknik ger
dock en del grundläggande kunskaper om datorers funktionssätt som kan vara mycket praktiska
att ha när man programmerar datorer. Helt utan dessa kunskaper måste man anta att en dator är
en magisk manick, eller själv skaffa sig en vag inre bild av en apparat som man inte förstår sig
på, och det är i bästa fall otillfredsställande, i sämsta fall rent förvirrande.
En dators innanmäte
En fullt fungerande, ytlig men korrekt mental bild av en dator är den som visas nedan. Datorn
består i grova drag av tre delar: en centralenhet (Central Processing Unit, CPU), ett minne och
ett antal in- och utenheter (Input/Output, I/O).
Minne
Program
Centralenhet
In- och utenheter
Register
Data
I minnet lagras binära bitar, oftast i grupper om 8, alltså 1 byte. Varje byte kan pekas ut av en
unik adress och kan både läsas och skrivas. Man kan tänka sig minnet som en lång lista med
numrerade rader där man på varje rad kan skriva in åtta binära bitar, och senare läsa vad som
skrevs och ändra det. Minnet i en modern dator innehåller typiskt många miljoner eller till och
med miljarder bytes. En miljon bytes benämns en megabyte, förkortat MB. En miljard bytes
betecknas gigabytes, GB1.
Centralenheten är den enhet som läser och skriver i minnet. Inuti centralenheten finns även ett
antal interna register som kan innehålla mindre mängder data. Läsning och skrivning i de interna registren går oftast mycket snabbare än läsning och skrivning i minnet.
In- och utenheterna är det som gör att datorn kan utföra något arbete. Det kan vara enheter för
att sköta inmatning från människor, som t ex mus och tangentbord, eller utmatning för presentation för människor, som bildskärm och ljudutgångar, men det kan även vara enheter för kommunikation med andra datorer, såsom nätverkskort och liknande. Enheter för extern datalagring,
som hårddisk och DVD- eller CD-spelare, är också in- och utenheter.
1. Egentligen skall man numera använda enheterna “mibi” och “gibi”, förkortat Mi och Gi, som står för
“mega-binary” respektive “giga-binary” i stället för SI-prefixen “mega” och “giga”, eftersom man
10
avser multiplar av 2 = 1024 och inte 1000, men den standarden har ännu inte (2005) fått genomslag.
Program
Centralenhetens arbete styrs av ett program. Programmet ligger också lagrat i minnet, och närcentralenheten exekverar ett program läser den alltså instruktionerna i minnet, och utför sedan
de begärda operationerna på data i samma minne. Principen att program och data ligger i samma
minne och att programmet lätt kan ändras gör en modern dator mycket flexibel och generell, och
de flesta datorer byggs på det sättet, även om det också är en källa till allvarliga och svårfunna
fel när program inte fungerar som de skall och av misstag tolkar program som data eller tvärtom.
En vanlig dator utför en enda operation i taget. Även om man numera snabbar upp datorer
genom att låta flera olika saker ske samtidigt så är hela grundkonstruktionen av ett datorprogram
något som förutsätter en viss ordning mellan operationerna som utförs. Detta kallas med ett
formellt ord för sekvens. En modern centralenhet exekverar flera miljarder instruktioner per
sekund, och om ett program bara var en rak sekvens av operationer skulle det inte rymmas program i minnet mer än för någon enstaka sekunds exekvering. De flesta program löser problem
av repetitiv struktur, så att samma operation utförs många gånger på en stor mängd data. På lägsta nivå i centralenheten finns två slags operationer som möjliggör detta, nämligen repetition
och villkor. Repetition medger att man tar om en sekvens av operationer och gör slingor (loopar)
i programmet. Villkoren gör att man kan avbryta repetitioner efter ett visst antal varv, när ett
visst villkor i data är uppfyllt eller när en inenhet talar om att den vill ha uppmärksamhet.
Sekvens, repetition och villkor samt förmågan att läsa och skriva i ett minne är de huvudsakliga
kännetecknen för en modern dators funktion. De grundläggande operationer som utförs är
mycket enkla, och kan i stort sett karaktäriseras som att man flyttar några enstaka bytes från ett
ställe i datorn till ett annat. De interna registren i centralenheten kan kopplas till funktionsblock
som kan utföra aritmetiska och logiska operationer, till exempel addera eller mulitplicera två
tal. På detta sätt kan en dator fås att behandla data och utföra nyttiga uppgifter.
Högnivå- och lågnivåprogrammering
Utan att gå in på exakt hur det ser ut någon speciell modern dator kan det ändå vara bra att se
hur en till synes enkel operation i ett högnivåspråk skulle kunna utföras som en sekvens av lågnivåinstruktioner. Antag att vi har två variabler i ett program och vill beräkna summan av dem
i en tredje variabel. Detta skulle göras med en tilldelningssats och ett aritmetiskt uttryck:
a, b, summa: Integer;
summa := a + b;
Detta program måste översättas (kompileras) till så kallad maskinkod som centralenheten kan
exekvera. Först måste de tre variablerna lagras på var sin adress i minnet. Centralenheten har
inget begrepp om variabelnamn, allt är bara adresser i minnet. När detta är gjort och summan
skall beräknas blir det en ganska lång sekvens av operationer för den enda tilldelningssatsen:
1. Läs heltalet på adressen för variabeln “a” och lagra det i ett register
2. Läs heltalet på adressen för variabeln “b” och lagra det i ett annat register
3. Beräkna summan av heltalen i de två registren och lägg resultatet i ett tredje register
4. Skriv heltalet i det tredje registret till adressen för variabeln “summa”
Endast en av fyra deloperationer i denna sekvens är själva beräkningen, resten är datatransporter
hit och dit. Detta är typiskt för de flesta program: det blir mycket flyttande av data, och en
förhållandevis liten del av programmet utför de egentliga operationer man är ute efter.
Även om det är fullt möjligt så är det naturligtvis inte särskilt inspirerande för människor att
tvingas programmera på denna låga nivå, och därför har man konstruerat högnivåspråk som
Ada, Java, C, C++ med flera. Översättningen från högnivåspråket till ett körbart program i
maskinkod görs automatiskt av en kompilator, som är ett program i datorn. Det är precis den
sortens repetitiva rutinarbete som datorer är bra på. Datorer kan inte riktigt programmera sig
själva, men de kan göra programmeringen mycket enklare för människor!