Binær algebra   
 

Opp

 

 

 

 

 

 

 

 

 

Guide til talsystem

Det finns 10 typar mennesker; dei som forstår binære talsystem og dei som ikkje gjer det. Me fortel deg korleis du kan skjønne programmerarar sin humor.

Sidan ei datamaskin er basert på såkalla binær algebra, bør ein programmerar kunne skjønne kva korleis ulike talsystem basert på det binære talsystemet fungerer. Korleis gjer me det leselig for menneske, og korleis reknar me med andre talsystem enn me er vane med?

Me er alle opplærte til å nytte eit talsystem som baserer seg på eit grunntal 10. Dette fell naturleg for oss, mest sannsynleg fordi me har 10 fingre, og eit titalssystem gjer det då svært lett å telje ved hjelp av fingrane. Fordi me er vane til å lese tal som om dei høyrer til titalssystemet; det binære talet 1001 vil dei fleste lese som om "tusen og ein", medan den korrekte uttalen vil vere "ein-null-null-ein". I titalssystemet tilsvarar dette talet 9.

Datamaskiner baserer seg som sagt på totalssystemet, eller binære tal. Dette gjer ein fordi ein då kan skilje mellom to enkle tilstandar; på og av, sant og usant, straum og ikkje-straum, eller 1 og 0. I denne guiden skal me vise deg korleis du kan konvertere tal frå vårt normale talsystem, titalssystemet, til andre talsystem, hovudsakleg to-tals og sekstenstal (heksadesimal). Me kjem og innom åttetalssystemet (oktale system).

Store delar av denne gjennomgangen krever ein del matematisk innsikt, men det bør vere mogleg å følgje den sjølv om ein ikkje har spesiell utdanning i matematikk. Med unntak av bruk av summeteikn, er dei matematiske uttrykka som ikkje vert forklart her, pensum på ungdomsskulen.

Generelt om talsystem

Som sagt nyttar menneska titalsystemet fordi det er praktisk for oss. Titalssystemet har ti ulike moglege verdiar for eit siffer, det som i daglegtale vert kalla tal. Tala me nytter normalt kjenner du nok til, nemlig 0-9. Det som er verdt å merke seg er at talet 0 (null) er med i alle talsystem, medan grunntalet er ikkje med. Dette betyr at det er umogleg å representere talet 2 i totalssystemet, utan å bruke meir enn eit teikn.

Dette medfører og eit lite problem for oss; ta t.d. det heksadesimale talsystemet, som har grunntal 16 (heksa - seks, desi - ti). For å då få nok tal til å representere eit slikt heksadesimalt tal, har me sett oss nødt til å ta i bruk bokstavar, spesifikt bokstavane a = 10, b = 11, c = 12, d = 13, e = 14 og f = 15. Igjen ser me at det høgaste sifferet i eit talsystem, er grunntalet minus ein. I denne innføringa vil eit siffer referere til ein enkelt teikn som representasjon av ein mengde (slik som t.d. "3"), medan eit tal vil vere ei samansetjing av siffer ("2394").

Alle talsystem har det til felles at å leggje på null foran talet, ikkje endrar verdien. Difor vil det binære talet 1001 vere det same som det binære talet 00001001.

Runde tal

Kva som er eit rundt tal vil vere ulikt alt ettersom kva talsystem som vert brukt. I titalssystemet er tal som 10, 100, 1000 osv. rekna som runde tal. Om ein reknar desse tala om til eit kva som helst anna talsystem, vil dei ikkje vere runde tal.

Eit tal vert tradisjonelt rekna som "rundt" ved at det kan skrivast som eit 10n-uttrykk, der n er større enn eller lik 1. Tala 101, 102 og 103 er som nemnt over runde tal, og som me ser er dei baserte på at talsystemet brukar 10 som grunntal.

I daglegtale omtalar ein ofte tal som er eit heiltalsmultiplum av grunntalet opphøgd i ein eksponent som runde tal, slik at i titalssystemet vil 20 og 30 vere runde tal i daglegtale. Dette er ulikt den matematiske definisjonen av eit rundt tal. Ein matematisk formel for eit "daglegdags rundt" tal er tal som passar med p * nm, der n er grunntalet i talsystemet, og p og m er vilkårlege tal.

For dei binære tala vil det difor vere andre runde tal, t.d. er 2, 4, 8 og 16 runde tal i totalssystemet. Når me kjem eit stykke vidare, nemlig til 210 = 1024, får me noko interessant. Dette er talet som liknar mest på det runde talet 1000 i titalssystemet, og har difor vorte nytta som grunntal for prefiksa kilo, mega, giga, tera, osv.. Sidan kilo ifølge SI-standarden er lik 1000, har dette opp igjennom tidene skapt trøbbel; t.d. var den siste disken eg kjøpte oppgitt til å innehalde 250GB. 250GB er ifølge SI-standarden 250 000 000 000 byte, 250 milliardar byte, og dette er akkurat så mykje som disken er berekna på å halde. Derimot måler operativsystemet mitt ein kilobyte som 1024 byte, ein megabyte som 1024 kilobyte, og ein gigabyte som 1024 megabyte. Når ein då reknar om, ser ein at storleiken på disken berre vil verte oppgitt som 232.8GB.

Difor har det no i det siste vore forsøkt innført ei ny type prefiks, som er kebi (Ki), mebi (Mi), gibi (Gi) og tebi (Ti). Dette gjer at størrelsen på disken min i gebibyte er 232.8GiB, medan den korrekte størrelsen i gigabyte er 250GB. Du kan lese meir om dette mange plassar på nettet, men her er ei fin oversikt.

Bruken av binære tal

Omtrent alle dagens datamaskiner baserer seg på bruk av ein byte som består av 8 bits. Ein bit står for binary digit, og representerer eit binært tal. Kvart binære tal kan ha verdien 1 eller 0, som me nemnte i introduksjonen. Fysisk i datamaskina vert desse verdiane representert ved at det er straum (1) eller at det ikkje er straum (0).

Kvar slik samansetning av 8 bits vert kalla ein byte. Denne byten representerer eit visst tal, som me igjen kan representere som t.d. ein bokstav. Først må me derimot finne ut kva tal denne samansetninga av bits står for. Dette gjer me ved ei enkel omrekning; og skal i første omgang bruke det binære talet 00101001, som er den binære representasjonen av talet 41 (i titalssystemet).

Kva sifferet eigentleg representerer som verdi, kjem ann på plassen i rekkefølgja det har; i titalssystemet representerer eit 9-tal på nest bakerste (plassen nest lengst mot høgre) verdien 90. Om me plaseserer talet på tredje bakerste posisjon, representerer det verdien 900. Måten me ser tala på, og automatisk legg dei saman til ein sum, er avhengig av måten me har lært oss å telje på. Sidan me alle har lært oss å nytte titalssystemet sidan me først lærte å telje, er det i første omgang svært vanskelig å tenke i andre talsystem.

Så, kva skal du eigentleg lære dette for? Kva kan du bruke andre talsystem til, som du ikkje kan frå før? I dagens programmeringsverden er det sjeldan ein får bruk for det direkte, men ofte indirekte. Ein kan t.d. ved hjelp av ein 8-bits byte lagre 8 ulike boolske verdiar. Talet (255)10 = (11111111)2 og gjer oss at alle boolske verdiane er sanne; talet (213)10 gjer oss (11010101)2, og på denne måten kan du representere 8 ulike boolske verdiar berre ved hjelp av tal i titalsystemet frå 0 til 255.

Det er og svært interessant å setje seg ned å rekne med t.d. binære tal; akkurat som me gjer med desimaltal. Det kan vere litt av ei utfordring i byrjinga, sidan me er vane til berre å rekne i eit talsystem. Addisjon og subtraksjon er lett, medan multiplikasjon og divisjon er ein god del verre å få teken på. Her er det anbefalt at du sett deg ned med eit ark, og ser kva du får til. Det er sjølvsagt juksing å konvertere til desimaltal mens du held på.

Heksadesimale tal

Heksadesimale tal (ofte forkorta til berre hex) er nesten like ofte brukt i datasamanheng enn binære tal er. Der folk flest nok har sett det, er i forbindelse med fargekodar i HTML (og ofte i programmeringsspråk). Fargekodar i HTML er oppgitt i rekkefølgja RGB (raud, grøn, blå), og mengden av kvar farge er representert ved hjelp av eit tosifra heksadesimalt tal. Eit tosifra heksadesimalt tal, kan lagre 16^2 = 256 forskjellige tal, dvs. tala frå 0 til og med (255)10. Ein farge som t.d. #FFE50F, vil altså innehalde så mykje raudfarge som mogleg, 229/255 av maks grønfarge, og 1/17-del av maks blåfarge. Fargen er ikkje spesielt fin. I programmeringsspråk er rekkefølgjen på denne lagringa ofte snudd om.

ASCII-tabellen, som er grunnsettet med teikn i dagens datamaskiner, består av 127 forskjellige teikn. Settet har seinare vorte utvida med 128 teikn, og er forskjellig avhengig av kva språk ein nyttar på PCen. Ei vanleg fil vert då lest byte for byte her i Europa, sidan bokstavane som desse språka nyttar finns stort sett på alle maskiner i området, og ligg i ASCII-tabellen (med dens tillegg). Dersom ein les dokument skrive på eit asiatisk språk, som t.d. japansk eller kinesisk, vil ein oppleve at ein byte i dokumentet faktisk ikkje alltid representerer noko i seg sjølv. Sidan dei har eit svært stort "alfabet", vil dei kreve fleire bytes for å representere eit teikn. Dette kallast MBCS (Multi-Byte Character Sets), og er ikkje noko me skal diskutere vidare her.

Derimot skal me sjå nærare på enkle filer; sidan kvar byte representerer eit teikn i ASCII-tabellen, veit me at me kan representere kvart teikn med ein tosifra heksadesimal verdi. Dette vert ofte brukt av debuggere (CPU- og minne-innsikt) og i programmeringseditorar, sjølv om sistnemnte er sjeldan å sjå no. Om ein ser på minneaddresser tildelt av Windows, vil ein og sjå at desse er oppgitt i hex.

Frå total til tital

Me skal bruke ein litt meir avansert versjon av barneskule-representasjonen av forklaringa, som faktisk er ei svært god forklaring; og lat oss sjå på talet 94807 i titalssystemet. Dette talet kallar me n, og når eg refererer til n[3], meiner eg det fjerde sifferet (frå høgre) i talet n. n[0] er difor i dette tilfellet lik 7, n[1] = 0, osv.. Ein kan då vere einig i at talet kan skrivast på følgjande måte; n = 94807 = 9 * 10^4 + 4 * 10^3 + 8 * 10^2 + 0 * 10^1 + 7 * 10^0. Dette ser ut som ein tungvindt skrivemåte, heilt fram til ein ser på det på ein meir matematisk måte;

der L er lengda på talet n minus 1. Grunnen til at me nyttar talet 10 her, er at det er snakk om titalssystemet. Me kan då byrje å sjå på det binære talet me nemte tidlegare, 00101001, som me kallar b. Dette talet er i totalssystemet, og difor vil me referere til det som (b)2 når talet er representert i totalsystemet, og (b)10 når det er representert i titalssystemet. Dette gjeld og når me skriv det som tal; (41)10 = (00101001)2. Dette gjer oss følgjande formel for å rekne eit tal i totalsystemet om til eit tal i titalssystemet;

Om me set inn verdiane for talet b som gitt over, vil me få følgjande uttrykk;

Me ser her at dei binære enkeltverdiane til talet n faktisk oppgjer om den respektive 2^n skal vere med i summen. Om me stryk så mykje me kan, får me følgjande forenkla uttrykk og svar;

For dei som ikkje er fortruleg med desse matematiske formlane, har me òg skrive dette på ein form for pseudokode;

desimal_tal := 0;
L := Length(binær_tal);
for i := 0 to (L - 1) do
  desimal_tal := desimal_tal + binær_tal[L - i] * 2^i;

Denne metoden for å gjere ting om til titalssystemet fungerer uansett kva talsystem du arbeidar frå. Ein byttar i så fall berre ut grunntalet i systemet med grunntalet i det nye systemet. På denne måten vil følgjande pseudo-kode gjere om eit tal frå det heksadesimale systemet;

desimal_tal := 0;
L := Length(hex_tal);
for i := 0 to (L - 1) do
  desimal_tal := desimal_tal + hex_tal[L - i] * 16^i;


 

Frå tital til total

For å gå den andre vegen, frå titalssystemet til eit gitt anna system, har ein annan framgangsmåte. I det binære talsystemet som me har nytta fram til no, har me forsøkt å benytte 8 siffer for å representere eit tal. Eit åtte-sifra binært tal kan maksimum halde verdien 28 - 1 = 255. Eit m-sifra n-ært tal kan maksimum halde verdien nm - 1.

Det første me må gjere når me skal gjere eit tal p i titalsystemet om til eit korkje som helst anna talsystem, er å bestemme kor mange siffer me treng. Dette gjer me ved å finne den minste verdien av nm - 1 som er større enn talet me skal gjere om. I pseudokode vil dette verte gjort på følgjande måte;

p := gitt tal;
n := talsystemets grunntal;
m := 0;
while p > (n^m - 1) do
  m := m + 1;

Når denne loopen er ferdig å køyre, vil m innehalde antallet siffer det n-ære talet må ha for å vere stort nok til å romme vårt tal.

Me er då nødt til å bryte ned talet me skal gjere om til dets respektive n^m-delar. Dette gjer me ved å loope gjennom tala, ved å bruke heiltalsdivisjon med dei største tala først. Me let så talet vere lik resten frå denne divisjonen vha. modulus (rest frå heiltalsdivisjon), og slik fortsett me heilt til me har tatt alle tala. Grunnen til at ut blir satt med m-1 i staden for berre i, er at me skal byrje å plassere tal frå venstre mot høgre, og ikkje frå høgre mot venstre som er normalt.

p := gitt tal;
n := talsystemets grunntal;
m := fra over, kor mange siffer me treng;
for i := (m - 1) downto 0 do
begin
  ut[m - i] := p div (n^i);
  p := p mod (n^i);
end;

Dette er eit uttrykk som er vanskelig (om ikkje umogleg) å uttrykke matematisk, og me kjem difor ikkje til å gjere det her. Ein kan derimot sjå på likningane på forrige side som ei forklaring på dette, utan å gå nærare inn på det her.

Det som ikkje er teke med i den gitte koden over, er kva ein gjer dersom grunntalet er større enn 10. Koden over vil ikkje hjelpe deg spesielt med dette, og for å ordne det, er du nødt til å gjere ut om til ein streng, og nytte ein funksjon som gjer om tal større enn 10 til dei respektive bokstavane. Eit forslag til ein slik funksjon følgjer under, skriven i Delphi;

function NumberToChar(Number: Integer): Char;
begin
  if Number < 10 then
    Result := IntToStr(Number)[1] // Gjer einaste Char i strengen
  else if (Number >= 10) and (Number <= 37) then
    Result := Chr(Number + 87) // ASCII #87 = a
  else
    Result := Chr(0); // hadde ingen bokstav for dette talet
end;

Sjølve koden i for konverteringa vil då få ei lita endring, og ei liknande endring må sjølvsagt plasserast i algoritmen som skal gå andre vegen;

  ut[m - i] := NumberToChar(p div (n^i));


 

Bruk av koden

Delphi

Delphi-koden som er skriven i denne artikkelen, får du tak i her. Koden er skriven som ein eigen klasse i Delphi, og den har to metodar som du kan bruke; FromSystem og ToSystem. Metodane har automatisk støtte for talsystem med meir enn ti tal, og nyttar bokstavane frå A til Z for å representere siffer som er større enn 10. Framande talsystem vert alltid representert som ein streng. Du brukar metodane på følgjande måte;

uses
  .., SCNumberConvert;
..
var

  SC: TSCNumberConvert;
  Tall: Integer;
  Str: String;
begin
  SC := TSCNumberConvert.Create;
  try
    Tall := SC.FromSystem('1101101', 2);
    Str := SC.ToSystem(255, 16);
  finally
    SC.Free;
  end;
end;

Det bør derimot nemnast at Delphi har innebygd metodar for å omgjere heiltal til både binære og heksadesimale tal. Dette er derimot ein meir generell metode som eg ikkje har sett nokonplass i Delphi, eller i andre språk. Me garanterer derimot ikkje for kvaliteten på koden, og bruk av den skjer på eige ansvar.

PHP

Om du er interessert i PHP-koden, finn du den pakka ned her. Koden er ei rask oversetjing frå Delphi, og er difor ikkje spesielt forskjellig frå det ein ser i Delphi. Det er derimot grei lesing dersom du likar PHP betre enn Delphi. Me måtte simulere ein heiltalsdivisjon ved hjelp av floor() og reell divisjon, sidan heiltalsdivisjon ikkje er innebygd i PHP.

Det er eit døme på korleis klassen kan nyttast i botn av fila. Eigentleg er det berre testar på at talsystemet konverterer talsystema rett. PHP-klassen har dei same metodane som Delphi-klassen har.

Lykke til!

Startside ] Opp ] [Søk]

Copyright © 2002 Øyvind Haugland
Sist endret:  13 januar 2019
 

  Interested in this stuff? Please write to:
 

HTML Counter            stats counter