Fra binært til heksadesimalt – forstå hvordan datamaskiner representerer tall
Datamaskiner lagrer ikke «123», «A» eller «-7» på samme måte som mennesker skriver dem. De lagrer mønstre av bits. Betydningen kommer fra hvordan vi velger å tolke disse mønstrene.
I dette kurset lærer du både å regne med ulike tallsystemer og å kjenne dem igjen i faktisk programvare og maskinvare.
Kurset starter fra første prinsipper og krever ingen forkunnskaper om binær, heksadesimal, assembler eller digital elektronikk.
Du bør være komfortabel med enkel heltallsregning: addisjon, subtraksjon, multiplikasjon og divisjon. Når vi trenger potenser eller annen notasjon, forklares den før den brukes videre.
Programmerings- og maskinvareeksempler brukes for å vise hvor representasjonene dukker opp i praksis. Du trenger ikke kunne C, Python eller assembler for å starte kurset.
Etter kurset skal du blant annet kunne:
Tallet tolv kan skrives på flere måter:
121100₂C₁₆14₈Verdien er den samme. Representasjonen er forskjellig.
Dette skillet er grunnlaget for resten av kurset.
I et posisjonssystem bestemmes verdien til et siffer både av selve sifferet og plasseringen.
Det desimale tallet 347 betyr:
3 × 10² + 4 × 10¹ + 7 × 10⁰.
Det binære tallet 1011₂ betyr:
1 × 2³ + 0 × 2² + 1 × 2¹ + 1 × 2⁰ = 11.
Basen forteller hvor mange forskjellige sifre systemet bruker før en ny posisjon må tas i bruk.
| Base | Navn | Sifre |
|---|---|---|
| 2 | binær | 0–1 |
| 8 | oktal | 0–7 |
| 10 | desimal | 0–9 |
| 16 | heksadesimal | 0–9, A–F |
I programmering brukes ofte prefikser som 0b1011 for
binært og 0x2A for heksadesimalt. Syntaksen varierer mellom
språk og assemblere.
Hva betyr 10 i base 2, base 8 og base 16?
I enhver base b betyr skrivemåten 10
verdien 1 × b¹ + 0 × b⁰.
Dermed er:
10₂ = 2₁₀10₈ = 8₁₀10₁₆ = 16₁₀Sifferrekkefølgen alene er ikke nok. Du må også vite hvilken base som brukes.
Binært bruker bare sifrene 0 og 1. Hver
posisjon representerer en potens av to.
Hva tror du bitmønsteret 10110110₂ betyr som et vanlig
unsigned heltall? Prøv å summere verdien av posisjonene som inneholder
1.
| Bitposisjon | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Verdi | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
For 10110110₂ er bit 7, 5, 4, 2 og 1 satt:
128 + 32 + 16 + 4 + 2 = 182.
Dermed er 10110110₂ = 182₁₀ når mønsteret tolkes som et
unsigned heltall.
En bit kan ha verdien 0 eller 1. Fire bits kalles ofte en nibble. Åtte bits utgjør normalt en byte.
Med n bits finnes 2ⁿ forskjellige
bitmønstre. Åtte bits gir derfor 256 mulige mønstre, fra
00000000 til 11111111.
For forutsigelsen er
10110110₂ = 128 + 32 + 16 + 4 + 2 = 182₁₀ som unsigned
heltall.
256 mulige mønstre betyr ikke at det største representerte tallet alltid er 255. Det gjelder bare når de åtte bitene tolkes som et unsigned heltall.
Det samme mønsteret kan senere tolkes som signed heltall, tegn, deler av en instruksjon, bitfelt eller noe helt annet.
Binært beskriver et bitmønster. Representasjonen og datatypen bestemmer hva bitmønsteret betyr. Dette skillet blir viktig gjennom resten av kurset.
10000001₂ som unsigned heltall?11111111₂ «er
255»?Heksadesimalt passer svært godt til binære datamaskiner fordi ett hex-siffer tilsvarer nøyaktig fire bits.
Se på 1101 0010₂. Kan du dele mønsteret i grupper på
fire bit og gjette den heksadesimale skrivemåten?
| Hex | Binær | Desimal |
|---|---|---|
| 0 | 0000 | 0 |
| 1 | 0001 | 1 |
| 2 | 0010 | 2 |
| 3 | 0011 | 3 |
| 4 | 0100 | 4 |
| 5 | 0101 | 5 |
| 6 | 0110 | 6 |
| 7 | 0111 | 7 |
| 8 | 1000 | 8 |
| 9 | 1001 | 9 |
| A | 1010 | 10 |
| B | 1011 | 11 |
| C | 1100 | 12 |
| D | 1101 | 13 |
| E | 1110 | 14 |
| F | 1111 | 15 |
Gruppen 1101₂ er D₁₆, og 0010₂
er 2₁₆. Derfor:
1101 0010₂ = D2₁₆.
Omvendt kan hvert hex-siffer ekspanderes til fire bits. For eksempel:
3A₁₆ = 0011 1010₂.
For forutsigelsen gir gruppene 1101₂ = D₁₆ og
0010₂ = 2₁₆, altså 1101 0010₂ = D2₁₆.
Hex endrer ikke verdien. Det er en kompakt skrivemåte for binære
bitmønstre, og koblingen er eksakt fordi 16 = 2⁴.
Denne egenskapen er grunnen til at hex brukes så mye i adresser, maskinkode, debugger-visninger, registerverdier, farger, protokoller og filformater.
Bokstavene A–F er ikke «tekst» inne i
tallet. De er sifre med verdiene 10–15 i base 16.
Heksadesimal er nyttig fordi mennesker kan lese lange binære mønstre mer kompakt uten å miste den direkte koblingen til bitene.
11111111₂ i hex.7C₁₆ i binær.Oktal bruker sifrene 0–7. Hvert oktalsiffer representerer nøyaktig tre binære bit, og derfor er oktal en praktisk kompakt skrivemåte for binære verdier.
Hva tror du binært 111 101 010 blir i oktal? Prøv å
oversette hver gruppe på tre bit før du leser videre.
Posisjonene i base 8 har vektene 8⁰, 8¹,
8² og så videre.
Eksempel:
572₈ = 5×8² + 7×8¹ + 2×8⁰ = 378₁₀.
Binært kan samme verdi grupperes tre og tre bit:
101 111 010₂ = 572₈.
For forutsigelsen får vi 111₂ = 7₈,
101₂ = 5₈ og 010₂ = 2₈. Dermed er
111 101 010₂ = 752₈.
Tre bit kan uttrykke verdiene 0–7. Derfor passer én gruppe på tre bit perfekt i ett oktalsiffer.
Oktal dukker fortsatt opp i Unix-lignende systemer. Filmodusen
755 består av tre grupper med rettighetsbit: eier, gruppe
og andre.
Tallet 755 i en Unix-filmodus leses normalt som oktal
når det brukes som numerisk rettighetsverdi. Det betyr ikke det desimale
tallet sju hundre og femtifem.
Oktal er ikke et annet slags tall. Det er en annen representasjon av samme verdi. Fordelen i databehandling er den direkte koblingen mellom ett oktalsiffer og tre bit.
111111₂ i oktal.17₈ i binær.Å konvertere betyr å endre representasjon uten å endre verdien.
Er 1010₂, 12₈, 10₁₀ og
A₁₆ fire forskjellige tall?
Fra en vilkårlig base til desimal summerer vi sifferverdi ganger posisjonsvekt:
2D₁₆ = 2×16 + 13 = 45₁₀.
Fra desimal til en annen base kan vi bruke gjentatt heltallsdivisjon og lese restene baklengs.
For 45 til binær:
Dermed 45₁₀ = 101101₂.
Mellom binær og hex grupperer vi fire bit:
0010 1101₂ = 2D₁₆. Mellom binær og oktal grupperer vi tre
bit: 101 101₂ = 55₈.
Nei: 1010₂ = 12₈ = 10₁₀ = A₁₆. De fire skrivemåtene
representerer samme verdi i forskjellige baser.
Binær fungerer som en nyttig bro mellom oktal og hex fordi gruppestørrelsene er faste.
Metodene er mekaniske, men målet er å kjenne igjen mønstre. Etter
hvert bør 1111₂ = F₁₆ = 15₁₀ være like naturlig som enkel
hoderegning.
Konverter 42₁₀ til binær og hex, FF₁₆ til
desimal, og 377₈ til binær.
Datamaskiner er fulle av størrelser som følger 2ⁿ. Å
kjenne de vanligste toerpotensene gjør adresser, bitmasker og
kapasiteter mye enklere å lese.
Her betyr n ganske enkelt hvor mange ganger 2 inngår som
faktor. For eksempel er 2³ = 2 × 2 × 2 = 8.
Hvor mange forskjellige verdier kan åtte bit representere?
Start med:
| n | 2ⁿ |
|---|---|
| 0 | 1 |
| 1 | 2 |
| 2 | 4 |
| 3 | 8 |
| 4 | 16 |
| 8 | 256 |
| 10 | 1024 |
| 16 | 65536 |
| 20 | 1048576 |
| 32 | 4294967296 |
Med n bit finnes 2ⁿ mulige bitmønstre. Åtte
bit gir derfor 256 mønstre, fra 0 til 255 når de tolkes som
unsigned.
1 KiB = 1024 bytes, mens SI-prefikset
1 kB = 1000 bytes. Tilsvarende er MiB og GiB binære
prefikser.
2⁸ = 256 betyr at åtte bit gir 256 forskjellige mønstre.
Det betyr ikke at den største unsigned verdien er 256. Når vi teller fra
0, blir den største verdien 255.
Åtte bit kan danne 2⁸ = 256 forskjellige bitmønstre, og
dermed representere 256 forskjellige verdier når en bestemt tolkning er
valgt.
Hex passer også naturlig: ett hexsiffer er fire bit, to hexsifre er én byte, og fire hexsifre dekker 16 bit.
Toerpotenser knytter sammen bitbredde, verdiområde, minnestørrelse og adresserom. Dette blir et gjennomgående verktøy resten av kurset.
Et unsigned heltall bruker alle bitene til størrelsen på en ikke-negativ verdi.
Hva er den største verdien som kan lagres i åtte bit?
Med n bit finnes 2ⁿ mønstre. For unsigned
heltall representerer de verdiene
0 ... 2ⁿ - 1.
Dermed er områdene blant annet:
| Bredde | Område |
|---|---|
| 8 bit | 0–255 |
| 16 bit | 0–65 535 |
| 24 bit | 0–16 777 215 |
| 32 bit | 0–4 294 967 295 |
11111111₂ = FF₁₆ = 255₁₀ som unsigned 8-bit-verdi.
En fast bitbredde kan ikke representere vilkårlig store verdier. I
aritmetikk modulo 2ⁿ vil en verdi som passerer maksimum gå
rundt dersom bare de nederste n bitene beholdes.
For eksempel blir 255 + 1 = 0 med 8-bit unsigned
wraparound.
Åtte bit gir 256 mulige mønstre, men området er 0–255 fordi null også bruker ett av mønstrene.
For et unsigned 8-bit heltall er maksimum 2⁸ − 1 = 255,
altså 11111111₂ = FF₁₆.
Bitmønsteret endrer ikke betydning av seg selv. FF₁₆ er
bare 255 dersom vi vet at mønsteret tolkes som unsigned.
Datatypen og bitbredden er en del av betydningen. Dette er avgjørende når du leser registre, protokollfelt, filer eller maskinkode.
Finn området for 12-bit unsigned og beregn hva 250 + 10
blir med 8-bit wraparound.
For å representere negative heltall må bitmønstrene få en avtalt signed-tolkning.
Kan bitmønsteret 11111111₂ bety både 255 og −1?
Historisk finnes flere representasjoner.
Sign-magnitude bruker én bit som fortegn og resten som størrelse. Det gir både +0 og −0.
Enerkomplement inverterer alle bit for å lage den negative verdien. Også dette gir to nullrepresentasjoner.
Toerkomplement er den dominerende representasjonen
for signed heltall i moderne datamaskiner. For n bit er
området
−2ⁿ⁻¹ ... 2ⁿ⁻¹ − 1.
For åtte bit er området −128 til 127.
Den øverste biten i toerkomplement er ikke bare et separat «minusflagg» som kan fjernes fra resten av tallet. Hele bitmønsteret deltar i representasjonen.
Det er også derfor området ikke er symmetrisk: åtte bit har én flere negativ verdi enn positive verdier.
Samme rå byte kan tolkes forskjellig:
FF₁₆ = 255 som unsigned 8-bitFF₁₆ = −1 som signed 8-bit toerkomplementIngen av tolkningene ligger «i» bitene alene.
Representasjon krever kontekst: bitbredde, signedness og kodingsregel. Debuggere og disassemblere må derfor vite hvilken type de skal vise.
Hva er området for signed 16-bit toerkomplement? Hvorfor er det én flere negativ verdi enn positive verdier?
Toerkomplement gjør at samme binære addisjonsmaskinvare kan brukes for positive og negative heltall.
Hvordan kan 11111011₂ representere −5?
For å finne representasjonen av −5 i åtte bit:
000001011111101011111011Dermed er FB₁₆ signed 8-bit −5.
En rask matematisk tolkning av et mønster med toppbit 1 er å ta den
unsigned verdien og trekke fra 2ⁿ:
251 − 256 = −5.
5 + (-5):
00000101 + 11111011 = 1 00000000
Carry utenfor åtte bit forkastes, og resultatet er null.
Negering gjøres med invertering + 1. Men −128 i signed 8-bit har ingen positiv motpart som passer i samme type; +128 ligger utenfor området. Dette er en viktig edge case.
For forutsigelsen er 11111011₂ mønsteret for −5 i 8-bit
toerkomplement: inverter 00000101₂ til
11111010₂ og legg til 1, som gir
11111011₂.
Carry og signed overflow er ikke det samme. CPU-er kan ha separate flagg fordi unsigned og signed tolkning stiller forskjellige spørsmål til samme bitresultat.
Toerkomplement er best forstått som aritmetikk modulo
2ⁿ, kombinert med en signed tolkning av halvparten av
bitmønstrene.
Representer −1, −42 og −128 som 8-bit hex. Tolk 80₁₆
både unsigned og signed.
Binær addisjon følger de samme prinsippene som desimal addisjon, men hvert siffer kan bare være 0 eller 1.
Hva blir 1111₂ + 0001₂ dersom registeret bare er fire
bit bredt?
De grunnleggende addisjonene er 0+0=0,
0+1=1, 1+0=1 og 1+1=10₂. Den
siste produserer en carry.
For fire bit:
1111 + 0001 = 1 0000
Hvis bare fire resultatbit beholdes, blir resultatet
0000, mens carry-bitten forteller at unsigned-resultatet
ikke passet.
Subtraksjon kan utføres direkte med borrow eller som addisjon av en toerkomplement-negativ verdi.
Mange CPU-er registrerer egenskaper ved resultatet. Vanlige flagg er:
Eksempel: signed 8-bit 7F₁₆ + 01₁₆ = 80₁₆. Her er
resultatmønsteret −128, men det matematiske resultatet +128 passer ikke.
Et overflow-flagg settes på arkitekturer som tilbyr et slikt flagg etter
denne operasjonen.
Flaggnavn og nøyaktig oppførsel er ikke identiske på alle CPU-er. Du må lese instruksjonssettets dokumentasjon for å vite hvilke flagg en bestemt instruksjon endrer og hva de betyr der.
Carry og signed overflow er heller ikke det samme: en operasjon kan sette det ene uten å sette det andre.
Med bare fire bit blir 1111₂ + 0001₂ = 1 0000₂.
Registeret beholder de fire lave bitene 0000₂, mens den
femte biten blir carry ut.
Carry og overflow tolker samme bitoperasjon ut fra henholdsvis unsigned og signed aritmetikk.
CPU-flagg lar etterfølgende instruksjoner ta beslutninger uten å gjenta hele beregningen. De er sentrale for sammenligning, hopp og aritmetikk med flere ord.
Beregn FF₁₆ + 01₁₆ og diskuter C, Z og V for en 8-bit
CPU som bruker de vanlige flaggdefinisjonene over.
Bitvise operasjoner arbeider på hvert bitpar uavhengig.
Hva blir 1010 AND 1100?
AND gir 1 bare når begge bit er 1. OR gir 1 når minst én bit er 1. XOR gir 1 når bitene er forskjellige. NOT inverterer hvert bit.
For A=1010₂ og B=1100₂:
A AND B = 1000A OR B = 1110A XOR B = 0110NOT A = 0101 dersom bredden er fire bitVenstreskift flytter bit mot høyere posisjoner. Innen fast bredde tilsvarer ett logisk venstreskift ofte multiplikasjon med 2 når ingen relevant bit går tapt.
Logisk høyreskift fyller med null. Aritmetisk høyreskift bevarer fortegnsbiten på signed toerkomplement-maskiner.
Rotate flytter bit rundt endene i stedet for å forkaste dem. Mange CPU-er har også rotate-through-carry, der carry-flagget inngår som en ekstra bit.
For forutsigelsen sammenlignes bitene parvis:
1010 AND 1100 = 1000.
Alle operasjonene må forstås med en eksplisitt bitbredde.
NOT 00000000 er 11111111 i åtte bit, men en
annen verdi dersom bredden er 16 bit.
Bitoperasjoner er grunnverktøy for registre, protokoller, grafikk, komprimering, kryptografiske primitiver og lavnivåprogrammering.
Beregn AND, OR og XOR for 3C₁₆ og 0F₁₆.
En bitmaske velger bestemte bit i en verdi uten å påvirke resten.
Et 8-bit register er 10100100₂. Hvordan setter du bit 0
uten å endre de andre bitene?
La MASK = 00000001₂.
value OR MASKvalue AND NOT MASKvalue XOR MASKvalue AND MASKFor flere bit kan masken inneholde flere ettall.
Et felt på bit 4–6 kan isoleres med en maske og deretter skiftes ned til bit 0 før det tolkes som et lite heltall.
Anta at bit 7 betyr ENABLE, bit 3 IRQ og bit 0 READY. Da kan
10001001₂ leses som tre uavhengige boolske flagg i samme
byte.
Når du tester en bit med value AND MASK, er poenget
vanligvis å sjekke om resultatet er null eller ikke-null. Resultatet
trenger ikke være tallet 1. Tester du for eksempel bit 5, kan et satt
resultat være 00100000₂.
NOT MASK må også forstås med en bestemt bitbredde. I et
8-bit register inverteres åtte bit; i en bredere datatype finnes flere
bit som også kan bli invertert.
For å sette bit 0 uten å endre de andre bitene kan vi OR-e med masken
00000001₂:
10100100₂ OR 00000001₂ = 10100101₂.
Hex gjør masker lettere å lese: 11110000₂ = F0₁₆ og
00001111₂ = 0F₁₆.
Bitfelt pakker mange små verdier effektivt. Masker lar programmet endre eller lese ett felt uten å ødelegge naboene.
Lag en maske for bit 2 og 5. Vis hvordan bit 5 kan toggles i
34₁₆.
Et minne kan modelleres som en lang rekke adresserbare byte. En adresse identifiserer en posisjon; en verdi kan bruke én eller flere påfølgende byte.
Hvis en 32-bit verdi starter på adresse 0x1000, hvilke
byteadresser opptar den?
I byte-adresserbart minne øker adressen normalt med én for hver byte:
0x1000, 0x1001, 0x1002, 0x1003.
En 32-bit verdi bruker fire byte og dekker derfor disse fire
adressene når den starter på 0x1000.
En offset er en forskyvning relativt til en
basisadresse. Hvis base er 0x2000 og offset er
0x34, er adressen 0x2034.
Med n adressebit kan 2ⁿ forskjellige
adresser kodes. Hvor mye data dette kan adressere avhenger av hva hver
adresse peker på. På et byte-adressert system betyr 16 adressebit opptil
65 536 forskjellige byteadresser.
Mange arkitekturer foretrekker eller krever at flerbyteverdier starter på bestemte adressegrenser. En 32-bit verdi kan for eksempel være naturlig aligned på en adresse delelig med 4.
Alignment er en maskinregel eller ytelsesegenskap, ikke en egenskap ved selve tallet.
Et adresserom er ikke det samme som mengden fysisk RAM som faktisk er installert. En CPU kan kunne uttrykke flere adresser enn systemet har fysisk minne for, og deler av adresserommet kan dessuten brukes til enheter, ROM eller andre formål.
For forutsigelsen opptar 32-bit-verdien fire byte:
0x1000, 0x1001, 0x1002 og
0x1003.
Hex passer svært godt til adresser fordi hvert hexsiffer er fire bit.
En 16-bit adresse kan skrives med fire hexsifre fra 0000
til FFFF.
Når du leser debugger- eller hex-editorvisninger, må du skille mellom adressen til data og verdien som ligger på adressen.
Hvilket adresseområde opptar en 16-byte blokk som starter på
0x3FF0? Hva er adressen base 0x8000 + offset
0x2A?
Endianness beskriver rekkefølgen byte fra en flerbyteverdi lagres i minnet. Det endrer ikke selve tallets verdi.
Hvordan kan verdien 0x12345678 ligge i fire byte fra
adresse 0x1000?
I big-endian lagres mest signifikante byte først:
| Adresse | Byte |
|---|---|
| 0x1000 | 12 |
| 0x1001 | 34 |
| 0x1002 | 56 |
| 0x1003 | 78 |
I little-endian lagres minst signifikante byte først:
| Adresse | Byte |
|---|---|
| 0x1000 | 78 |
| 0x1001 | 56 |
| 0x1002 | 34 |
| 0x1003 | 12 |
Bitrekkefølgen inne i den vanlige skriftlige byteverdien snus ikke. Endianness handler her om rekkefølgen på byte i en flerbyteverdi.
Motorola 68000-familien forbindes med big-endian, mens x86 bruker little-endian. Protokoller kan definere sin egen byteorden uavhengig av CPU-en.
Ser du bytefølgen 78 56 34 12, kan den være
32-bitverdien 0x12345678 dersom formatet sier
little-endian. Uten type, bredde og byteorden er rå byte tvetydige.
For forutsigelsen kan 0x12345678 ligge som
12 34 56 78 i big-endian eller 78 56 34 12 i
little-endian fra den laveste adressen. Byteorden er en del av
representasjonen.
En hex-dump viser byte i stigende minneadresse, ikke nødvendigvis sifrene i den rekkefølgen vi skriver den numeriske verdien.
Endianness blir viktig når data flyttes mellom CPU-er, nettverk, binærfiler, emulatorer og debuggerverktøy.
Skriv 0xA1B2C3D4 som fire byte i big- og
little-endian.
BCD lagrer desimalsifre som binære koder i stedet for å representere hele tallet som ett binært heltall.
Hvordan kan desimaltallet 42 lagres dersom hvert desimalsiffer får fire bit?
I packed BCD bruker hvert desimalsiffer én nibble:
42₁₀ -> 0100 0010₂ -> 0x42
Dette er ikke det samme som vanlig binær representasjon av 42, som er
00101010₂ = 0x2A.
Bare nibbleverdiene 0–9 er gyldige desimalsifre; A–F brukes normalt ikke som BCD-sifre.
I unpacked BCD bruker hvert siffer typisk en hel byte, mens packed BCD legger to sifre i én byte.
BCD har vært viktig i kalkulatorer, klokker, tellere, økonomisystemer og eldre maskiner fordi desimalsifre kan bevares direkte og vises uten binær/desimal avrundingsproblematikk.
0x42 betyr ikke automatisk desimalt 42. Som et vanlig
binært heltall er 0x42 = 66₁₀; bare når formatet sier
packed BCD tolkes de to nibblene som desimalsifrene 4 og 2.
Med fire bit per desimalsiffer blir 4 kodet som 0100 og
2 som 0010. Packed BCD for 42 blir derfor
0100 0010₂ = 0x42.
Hexdumpen 42 kan bety vanlig binær verdi 66 eller packed
BCD-verdi 42. Formatkontekst avgjør.
BCD demonstrerer igjen hovedideen i kurset: samme bitmønster kan ha forskjellige betydninger under forskjellige representasjonsregler.
Skriv 1987 som packed BCD. Hvor mange byte kreves? Hvorfor kan ikke
en hex-editor alene fortelle deg om byten 42 er binær 66
eller BCD 42?
Fixed-point representerer brøktall med en implisitt fast plassering av binærpunktet.
Hvis et 8-bit mønster har fire heltallsbit og fire brøkbit, hvilken
verdi har 00111000₂?
I et unsigned Q4.4-format (fire heltallsbit og fire brøkbit) har de
fire nederste bitene vektene 2⁻¹, 2⁻²,
2⁻³ og 2⁻⁴.
0011.1000₂ = 3 + 1/2 = 3.5.
Det lagrede heltallet er 56. Skalafaktoren er 2⁴ = 16,
så den virkelige verdien er 56 / 16 = 3.5.
Med f brøkbit er oppløsningen 1 / 2^f.
Toerkomplement kan kombineres med et fast binærpunkt. Bitmønsteret
tolkes først som et signed heltall og skaleres deretter med
1 / 2^f.
Fixed-point gir forutsigbar presisjon og kan være effektivt på mikrokontrollere uten rask floating-point-maskinvare. Til gjengjeld må programmereren holde kontroll på skalering, range og overflow.
Med fire brøkbit ligger binærpunktet etter de fire øverste bitene:
0011.1000₂ = 3 + 1/2 = 3.5.
Binærpunktet lagres ikke som et eget symbol. Formatdefinisjonen forteller hvor det ligger.
Fixed-point er egentlig heltallsaritmetikk med en avtalt skala. Det gjør forbindelsen til bitbredde, toerkomplement og overflow svært tydelig.
Hva er oppløsningen med åtte brøkbit? Tolk heltallsverdien 384 med åtte brøkbit.
Floating-point lar binærpunktet «flyte» ved å lagre en signifikand sammen med en eksponent.
Hvorfor kan et svært stort og et svært lite tall begge representeres i samme 32-bit format?
IEEE 754 binary32 bruker 32 bit fordelt på:
For normale verdier representerer feltet i prinsippet
(-1)^sign × significand × 2^exponent.
Eksponenten lagres med bias, og den ledende 1-biten i normale binære verdier er implisitt.
Standarden definerer også spesialverdier som +0, −0, positive/negative infinity, NaN og subnormale tall.
1.0 = 0x3F800000Binary32-mønsteret 0x3F800000 er:
0 01111111 00000000000000000000000
127 − 127 = 01.0₂Dermed blir verdien (+1) × 1.0₂ × 2⁰ = 1.0.
For å konvertere en brøk kan vi gange brøkdelen med 2 gjentatte ganger og lese av heltallsdelen:
0.5 × 2 = 1.0 → første bit er 1, altså
0.5₁₀ = 0.1₂0.25 × 2 = 0.5, deretter 0.5 × 2 = 1.0 →
0.25₁₀ = 0.01₂For 0.1₁₀ stopper prosessen ikke: bitmønsteret
fortsetter periodisk. Derfor finnes ingen endelig binær brøk som er
nøyaktig lik en tidel.
Brøken 1/10 har ingen endelig binær brøkrepresentasjon, på samme måte som 1/3 ikke har en endelig desimalrepresentasjon. Verdien må derfor avrundes til nærmeste representerbare floating-point-verdi.
Når flere slike avrundede verdier brukes i aritmetikk, kan resultatet
avvike litt fra det matematiske desimaltallet. I vanlig
binary64-aritmetikk blir for eksempel 0.1 + 0.2 typisk
representert som omtrent 0.30000000000000004, ikke nøyaktig
0.3.
Flere eksponentbit gir stor range; flere signifikandbit gir større presisjon. Floating-point gir derfor et kompromiss, ikke «vilkårlig presise reelle tall».
0x3F800000 viser konkret at sign-, eksponent- og
fraction-feltene sammen gir verdien 1.0. Eksemplet med 0.1 viser
samtidig hvorfor ikke alle desimalbrøker kan lagres nøyaktig.
Et floating-point-bitmønster kan ikke tolkes som et vanlig heltall og forventes å beholde samme numeriske verdi. Feltstrukturen definerer representasjonen.
IEEE 754 gjør floating-point portabelt og veldefinert, men programmer må fortsatt forstå avrunding, spesialverdier og sammenligning.
Hvorfor er testing av floating-point-likhet ofte mer komplisert enn testing av heltallslikhet?
Bitmønstre brukes til langt mer enn vanlige heltall. Dette kapitlet viser fire viktige eksempler: Gray-kode, biased/excess-notasjon, tegnkoder og packed data.
Må bitmønsteret 01000001 alltid bety tallet 65?
I binær-reflektert Gray-kode skiller naboverdier seg med nøyaktig én bit. Dette er nyttig når flere bit ikke bør endre seg samtidig, for eksempel i enkelte posisjonssensorer og enkodere.
De første 3-bit Gray-kodene er:
| Desimal | Binær | Gray |
|---|---|---|
| 0 | 000 | 000 |
| 1 | 001 | 001 |
| 2 | 010 | 011 |
| 3 | 011 | 010 |
| 4 | 100 | 110 |
Fra binær verdi b kan Gray-koden beregnes som
g = b XOR (b >> 1).
En eksponent kan lagres som et unsigned felt med en fast bias. Hvis bias er 127, representerer lagret felt 130 eksponenten (130-127=3).
Dette er prinsippet som brukes for eksponentfeltet i IEEE 754 binary32, selv om spesielle feltverdier har egne betydninger.
ASCII gir bokstaven A kodeverdien 65, altså
0x41. Unicode definerer code points, for
eksempel U+0041 for A. En encoding som UTF-8 bestemmer deretter hvilke
byte som faktisk lagres.
Code point og byteencoding er derfor ikke det samme.
En byte kan deles i bitfelt. Et tenkt statusregister kan bruke:
Da er byten ikke «ett tall» i vanlig forstand, men flere felt pakket sammen.
Nei. 01000001₂ = 0x41 er 65 som unsigned heltall, men
samme mønster kan for eksempel representere ASCII-tegnet
A.
0x41 kan tolkes som unsigned 65, signed 65, ASCII-tegnet
A, deler av en instruksjon eller et felt i en struktur. Konteksten
bestemmer betydningen.
Datamaskinen lagrer bit. Typer, protokoller, instruksjonssett og filformater gir bitene semantikk.
Beregn Gray-koden for binær 1010. Hva er den virkelige
eksponenten når et excess-127-felt inneholder 124?
Assembly gjør skillet mellom verdi, syntaks, bitbredde og maskinbetydning svært synlig. Samme tall kan være en immediate konstant, adresse, maske, offset eller del av en maskininstruksjon.
Betyr $10, #10, 0x10 og
10h alltid det samme?
Assemblerdialekter bruker forskjellige notasjoner. Vanlige eksempler er:
42 — ofte desimal0x2A — vanlig hexnotasjon$2A — hex i mange 6502- og 68k-assemblere2Ah — hex i enkelte Intel-lignende syntakser%101010 — binær i enkelte assemblereSyntaksen må alltid leses i kontekst av den aktuelle assembleren.
Et tall i en instruksjon kan beskrive selve verdien eller stedet verdien skal hentes fra.
LDA #$2A betyr typisk last den umiddelbare verdien
0x2A.
LDA $2A betyr derimot les fra en minneadresse etter
assemblerens og instruksjonens adresseringsregler.
MOVE.B #$2A,D0 bruker #$2A som immediate
verdi.
MOVE.B $002A,D0 refererer til minne.
Suffix som .B, .W og .L gjør
også datastørrelsen eksplisitt i mange 68k-assemblerdialekter.
AVR har egne registre og instruksjonsbegrensninger. En konstant kan for eksempel lastes med:
LDI R16, 0x2A
Tallene i kildekoden må fortsatt passe operandens tillatte range og instruksjonsencoding.
x86 har flere assemblerdialekter. Intel- og AT&T-syntaks kan uttrykke samme maskininstruksjon forskjellig. Derfor er det farlig å lære én tekstlig form som om den var «assembly-syntaksen».
Et register har en definert bredde. Skriver du et større tall enn feltet eller instruksjonen kan representere, må assembleren enten avvise det, redusere det etter definerte regler eller velge en annen encoding.
Bitbredde bestemmer også hvordan samme bitmønster kan tolkes signed eller unsigned.
Assembly bruker ofte tall som bitmasker:
AND #$0F
kan brukes til å beholde de fire laveste bitene i en byte, avhengig av ISA og syntaks.
En disassembler starter med rå byte og forsøker å tolke dem som instruksjoner for en bestemt ISA og startadresse. En byte som er data i ett område kan derfor bli vist som en tilsynelatende instruksjon dersom verktøyet får feil kontekst.
Nei. Prefikser og suffikser er assembler-syntaks, og betydningen av blant annet `# Tall i assembly
Assembly gjør skillet mellom verdi, syntaks, bitbredde og maskinbetydning svært synlig. Samme tall kan være en immediate konstant, adresse, maske, offset eller del av en maskininstruksjon.
Betyr $10, #10, 0x10 og
10h alltid det samme?
Assemblerdialekter bruker forskjellige notasjoner. Vanlige eksempler er:
42 — ofte desimal0x2A — vanlig hexnotasjon$2A — hex i mange 6502- og 68k-assemblere2Ah — hex i enkelte Intel-lignende syntakser%101010 — binær i enkelte assemblereSyntaksen må alltid leses i kontekst av den aktuelle assembleren.
Et tall i en instruksjon kan beskrive selve verdien eller stedet verdien skal hentes fra.
LDA #$2A betyr typisk last den umiddelbare verdien
0x2A.
LDA $2A betyr derimot les fra en minneadresse etter
assemblerens og instruksjonens adresseringsregler.
MOVE.B #$2A,D0 bruker #$2A som immediate
verdi.
MOVE.B $002A,D0 refererer til minne.
Suffix som .B, .W og .L gjør
også datastørrelsen eksplisitt i mange 68k-assemblerdialekter.
AVR har egne registre og instruksjonsbegrensninger. En konstant kan for eksempel lastes med:
LDI R16, 0x2A
Tallene i kildekoden må fortsatt passe operandens tillatte range og instruksjonsencoding.
x86 har flere assemblerdialekter. Intel- og AT&T-syntaks kan uttrykke samme maskininstruksjon forskjellig. Derfor er det farlig å lære én tekstlig form som om den var «assembly-syntaksen».
Et register har en definert bredde. Skriver du et større tall enn feltet eller instruksjonen kan representere, må assembleren enten avvise det, redusere det etter definerte regler eller velge en annen encoding.
Bitbredde bestemmer også hvordan samme bitmønster kan tolkes signed eller unsigned.
Assembly bruker ofte tall som bitmasker:
AND #$0F
kan brukes til å beholde de fire laveste bitene i en byte, avhengig av ISA og syntaks.
En disassembler starter med rå byte og forsøker å tolke dem som instruksjoner for en bestemt ISA og startadresse. En byte som er data i ett område kan derfor bli vist som en tilsynelatende instruksjon dersom verktøyet får feil kontekst.
, #, 0x og h avhenger av
språket og konteksten. Du må lese syntaksreglene for den aktuelle
assembleren.
Assemblerkilden inneholder menneskevennlige symboler og tallnotasjoner. CPU-en ser den ferdig kodede instruksjonens bitfelt.
Tallforståelse er grunnleggende for assembly fordi operander, adresser, registre, masker, offsets og instruksjonsencoding alle er begrensede bitfelt.
Hvorfor er #$2A og $2A forskjellige i mange
klassiske assemblere? Hvorfor må du vite assemblerdialekten før du
tolker 10h?
C og Python kan uttrykke de samme binære og heksadesimale verdiene, men heltallsmodellene deres er svært forskjellige.
Vil 255 + 1 alltid gi samme resultat i C og Python?
Heksadesimale og desimale literaler fungerer likt i begge språk.
Binære literaler som 0b101010 støttes i Python og fra C23 i
C (mange kompilatorer, blant annet GCC og Clang, har lenge støttet dem
som utvidelse):
0b101010
0x2A
42
I C bestemmes typen også av språkets type- og literalregler. I Python
er int ikke begrenset til en fast 8-, 16-, 32- eller 64-bit
bredde.
Når maskinbredde betyr noe, er typene fra
<stdint.h> nyttige:
#include <stdint.h>
uint8_t a = 0xFF;
uint16_t b = 0x1234;
int32_t c = -42;Eksakt-bredde-typene finnes når implementasjonen tilbyr en heltallstype med den aktuelle bredden.
Unsigned C-aritmetikk følger modulo 2ⁿ for typens
bredde. Signed overflow må ikke behandles som om C lover samme
wraparound; signed integer overflow er ikke definert på samme måte av
språket.
x = 255
print(x + 1) # 256Python-int vokser etter behov innen praktiske
ressursgrenser. Hvis du vil simulere et 8-bit register, må du selv
innføre bredden, for eksempel:
x = (255 + 1) & 0xFFDa blir resultatet 0.
value & 0x0F
value | 0x80
value ^ 0x01
value << 1
value >> 1
Betydningen av høyreskift for negative verdier og effekten av promotions/typebredder må vurderes etter språket og typen. Ikke flytt maskinantakelser ukritisk mellom språk.
Python:
int("101010", 2)
int("2A", 16)
int("0x2A", 0)C bruker blant annet funksjoner som strtoul, der base og
feilhåndtering må behandles eksplisitt.
Python:
f"{42:b}" # 101010
f"{42:02X}" # 2A
f"{42:08b}" # 00101010I C brukes formatmakroer fra <inttypes.h> når
portabel formatering av fixed-width heltall er viktig.
Nei. Resultatet avhenger av type og språkregler. Python-heltall vokser normalt etter behov, mens aritmetikk i en begrenset C-heltallstype kan ha andre regler. Derfor må både type og språksemantikk være kjent.
Kildekoden 0xFF beskriver en numerisk literal. Om du
senere tolker eller lagrer den som 8-bit signed, 8-bit unsigned eller en
større type er en egen beslutning.
Programmeringsspråk legger regler oppå bitrepresentasjonene. God lavnivåkode krever at du forstår både maskinmodellen og språkmodellen.
Hvorfor gir (255 + 1) & 0xFF verdien 0 i Python?
Hvorfor bør du ikke anta at signed C-overflow fungerer på samme
måte?
I digital elektronikk kobles bitmønstre til fysiske signaler, registre, målinger og protokoller. Tallene er ikke bare matematikk: de styrer pinner og beskriver virkelige måleverdier.
Hvis et 8-bit GPIO-register inneholder 0x81, hvilke bit
er satt?
En logisk 0 eller 1 er en abstraksjon. Den fysiske kretsen bruker spenningsområder som tolkes som LOW og HIGH. Nøyaktige terskler avhenger av komponent og datablad.
Et signal kan også være active-low. Da betyr det
aktive nivået logisk/funksjonelt «på» når den elektriske linjen er LOW.
Navn som /RESET, RESET_n eller lignende brukes
ofte, men konvensjonen varierer.
Tenk et register:
PORT = 1010 0001₂ = 0xA1
Hver bit kan styre en separat funksjon. En maske kan endre én bit uten å uttrykke hele registerverdien på nytt.
Sett bit 3:
PORT |= (1 << 3)
Nullstill bit 3:
PORT &= ~(1 << 3)
Toggle bit 3:
PORT ^= (1 << 3)
På virkelig hardware må du lese databladet: enkelte registre har egne SET/CLEAR-registre, write-one-to-clear-flagg eller andre regler som gjør en vanlig read-modify-write feil.
Et registerkart oppgir typisk:
En verdi som 0x82 er først nyttig når vi vet hvilket
register den tilhører og hva hvert bitfelt betyr.
En idealisert N-bit ADC har 2^N digitale koder. En
10-bit ADC har 1024 koder, normalt 0–1023.
For en enkel idealisert unipolar modell kan en kode omregnes omtrent som:
V ≈ code / (2^N - 1) × Vref
Den nøyaktige overføringsfunksjonen, referansen, toleranser og endpoint-definisjonen må hentes fra databladet. Formelen over er derfor en læringsmodell, ikke en universell ADC-lov.
En DAC gjør motsatt retning: en digital kode påvirker en analog utgang. Også her bestemmer bitbredde antall koder og dermed den idealiserte kvantiseringen.
En logic analyzer samler digitale samples. Verktøyet kan vise dem som:
Byte 10100101₂ = 0xA5 kan være kommando, adresse, flagg
eller data. Protokollen gir semantikken.
Ved serielle protokoller må du også kjenne blant annet bitrekkefølge, klokking, framing og byteorden der flerbytefelt brukes.
For forutsigelsen er 0x81 = 1000 0001₂, så bit 7 og bit
0 er satt.
Hardware-registre og protokoller er praktiske eksempler på hele kursets hovedidé: bitmønster + avtalt representasjon = mening.
Når du leser et datablad, oversetter du kontinuerlig mellom bitposisjoner, masker, hex, desimale størrelser og fysisk funksjon.
Hvilke bit er satt i 0x81? Hvor mange koder har en
12-bit ADC? Hvorfor bør du ikke anta at alle statusflagg kan nullstilles
med vanlig read-modify-write? Hvorfor må en ADC-formel alltid
kontrolleres mot databladet?
Nettverkspakker og binære filer består av byte. For å forstå dem må vi vite hvilke byte som hører sammen, hvilken byteorden som brukes, og hva hvert felt betyr.
Bytefølgen 12 34 representerer et 16-bit tall. Er
verdien 0x1234 eller 0x3412? Skriv hypotesen
din før du går videre.
Mange Internett-protokollfelt med flere byte bruker network byte order, som er big-endian.
Et 16-bit felt med verdien 0x1234 ligger da som:
12 34
Dette betyr ikke at maskinen som sender eller mottar pakken selv må være big-endian. Protokollens representasjon og CPU-ens interne representasjon er separate spørsmål.
En IPv4-adresse er 32 bit og vises vanligvis som fire desimale oktetter.
192.168.1.10
tilsvarer byte:
C0 A8 01 0A
Punktnotasjonen er menneskevennlig; pakken inneholder byte.
En vanlig 48-bit MAC-adresse skrives ofte som seks hex-byte:
02:12:34:56:78:9A
Her er hex spesielt nyttig fordi hvert byte kan skrives med nøyaktig to hex-sifre.
En pakke kan inneholde:
Noen felt er bitfelt. Andre er heltall over flere byte. Dokumentasjonen bestemmer bredde, byteorden og semantikk.
Binære filformater starter ofte med karakteristiske byte som hjelper programvare å identifisere formatet.
For eksempel kan en fil begynne:
89 50 4E 47 0D 0A 1A 0A
Dette er den åtte byte lange signaturen i starten av en PNG-fil. En signatur er nyttig identifikasjon, men alene beviser den ikke at resten av filen er gyldig.
Tenk denne fiktive posten:
01 03 12 34 41 42 43
Formatet sier:
Da får vi:
0x123441 42 43, som kan tolkes som ASCII
ABCUten formatbeskrivelsen er dette bare sju byte.
Binære parsere arbeider ofte med offsets:
offset + field_length
Et felt som starter ved offset 4 og er 2 byte langt bruker byte 4 og 5. Neste felt starter ved offset 6.
Feil bredde eller feil offset forskyver resten av tolkningen.
For forutsigelsen finnes det ikke ett riktig svar uten byteorden:
big-endian gir 0x1234, mens little-endian gir
0x3412.
Samme byte kan representere del av en IP-adresse, et tegn, et flaggfelt, et heltall eller rå payload.
Hex-dumpen viser lagrede eller overførte byte. Format- eller protokollspesifikasjonen forteller hvordan byte skal grupperes og tolkes.
Hvordan lagres 0xBEEF som et big-endian 16-bit felt?
Hvilke byte representerer IPv4-adressen 127.0.0.1? Hvorfor
er en magic number ikke nok til å validere en hel fil?
Når et program krasjer, en fil er ukjent eller et gammelt system mangler dokumentasjon, møter vi ofte rå byte før vi kjenner strukturen. Tallrepresentasjon blir da et analyseverktøy.
Du finner bytene:
48 65 6C 6C 6F 00
Er dette seks heltall, maskinkode eller tekst?
Skill mellom det du ser og det du antar.
En hex-editor eller memory dump kan vise:
At en tekstkolonne viser lesbare tegn er et spor, ikke et bevis på at feltet faktisk er tekst.
Nyttige observasjoner kan være:
Hypoteser bør testes mot flere eksempler.
Bytene
34 12
kan være 0x3412 som big-endian eller 0x1234
som little-endian.
Hvis en hypotese sier at feltet er en lengde, kan vi kontrollere om verdien passer med resten av datastrukturen.
Debuggere viser ofte adresser i hex. Forskjellen mellom to adresser er en størrelse:
0x1040 - 0x1000 = 0x40 = 64
Dette er nyttig når vi undersøker buffere, tabeller og strukturer.
En disassembler tolker byte som instruksjoner for en bestemt CPU og et bestemt startpunkt.
Det betyr ikke automatisk at alle viste byte faktisk er kode. Data som tolkes som instruksjoner kan produsere syntaktisk gyldig, men meningsløs disassembly.
Spør derfor:
I eldre og innebygde systemer kan kode, tabeller, strenger og konstanter ligge tett sammen.
En sekvens som 41 42 43 44 kan være teksten
ABCD, fire små heltall, deler av større tall eller
instruksjonsbyte. Kontekst avgjør.
Tenk denne fiktive dumpen:
03 00 08 00 41 42 43 00
En hypotese kan være:
Dette er foreløpig bare en modell. Vi styrker den ved å undersøke flere poster og se om feltene oppfører seg konsekvent.
En effektiv metode i egne systemer og testdata er å endre én kjent verdi om gangen og sammenligne før/etter.
Hvis et flagg slås på og nøyaktig én bit endres, har vi et godt spor. Hvis en teller økes og et bestemt felt følger den, lærer vi mer om representasjonen.
For forutsigelsen er flere tolkninger mulige uten kontekst. Som
ASCII/UTF-8 gir 48 65 6C 6C 6F 00 teksten
Hello etterfulgt av en nullbyte; det er en sterk hypotese,
ikke et bevis alene.
Reverse engineering av data handler ofte mindre om å «gjette riktig» med én gang og mer om å formulere hypoteser som kan motbevises eller styrkes.
Kunnskap om baser, signedness, byteorden, bitfelt, adresser og tekstkoding gjør rå byte lesbare. Men kontekst og flere observasjoner er nødvendig for å skille en plausibel tolkning fra en dokumentert struktur.
Hvorfor kan disassembly av data se ut som gyldig kode? Hva kan
00 01 bety under ulike byteordener? Hvorfor er flere
eksempler bedre enn én dump når vi prøver å finne en struktur?
Binær, oktal, desimal og heksadesimal dominerer moderne datateknikk, men et posisjonssystem kan i prinsippet bruke mange andre baser. Andre valg viser hvorfor en base er en representasjonsregel, ikke en egenskap ved selve tallet.
Hva tror du 10 betyr i base 3? Skriv svaret før du går
videre.
Base 3 bruker sifrene 0, 1 og 2.
102₃ = 1 × 9 + 0 × 3 + 2 = 11₁₀
Ternære systemer er interessante fordi de viser at digital representasjon ikke matematisk krever akkurat to symboler. Det har også eksistert forskning og maskiner basert på ternær logikk.
Base 12 har tolv sifferverdier per posisjon. I dette kurset bruker vi
A for verdien ti og B for verdien elleve, i
tillegg til 0–9. Dermed er for eksempel A₁₂ = 10₁₀ og
B₁₂ = 11₁₀.
Tolv har mange delere: 2, 3, 4 og 6. Det gjør enkelte brøker kompakte.
I base 12 er for eksempel en halv:
0.6₁₂
fordi 6/12 = 1/2.
Vigesimale systemer bruker base 20 og finnes historisk i flere språk og kulturer.
Poenget for oss er representasjon: når basen er 20, betyr
10₂₀ tjue, ikke ti.
Base 36 er praktisk i tekst fordi sifrene 0–9 og bokstavene A–Z kan representere 36 sifferverdier.
Da er:
Z₃₆ = 35₁₀
og:
10₃₆ = 36₁₀
Base 36 kan gi kompakte tekstlige representasjoner av ikke-negative heltall, men store/små bokstaver og alfabet må defineres av formatet.
Seksagesimale systemer har svært gamle historiske røtter. Spor av base 60 er fortsatt synlige i tids- og vinkelmåling:
Dette er ikke et rent moderne posisjonssystem i alle disse bruksområdene, men viser hvordan en valgt oppdeling kan leve videre svært lenge.
Basens faktorer påvirker hvilke brøker som får endelige representasjoner.
I base 10 er 1/2 = 0.5 og 1/5 = 0.2 endelige, mens 1/3 repeterer.
I base 2 er 1/2 endelig, mens 1/10₁₀ ikke har en endelig binær brøk. Dette knytter direkte tilbake til flyttallskapitlet.
Base og lagringsbredde er forskjellige konsepter.
Et tall kan skrives i base 36 som tekst og likevel lagres internt som et vanlig binært heltall. Omvendt kan en binær byte vises som desimal, hex eller base 36 uten at byteverdien endres.
Datamaskinhistorien inneholder andre representasjoner enn dagens vanligste binære mønstre. Noen maskiner brukte desimalorientert aritmetikk eller andre ordstørrelser og tegnkodinger enn vi forventer i dag.
Derfor bør historiske data alltid leses ut fra den aktuelle maskinens dokumenterte representasjon, ikke moderne antakelser.
For forutsigelsen betyr 10₃ tre:
1 × 3¹ + 0 × 3⁰ = 3.
10 kan representere 2, 3, 8, 10, 12, 16, 20, 36, 60
eller en annen verdi avhengig av basen.
Et posisjonssystem er en avtale om siffer, posisjonsvekter og base. Tallet er den abstrakte verdien; notasjonen er representasjonen.
Hva er 10₁₂ i desimal? Hva er Z₃₆? Hvorfor
kan base 12 representere enkelte vanlige brøker mer kompakt enn base
10?
Dette prosjektet samler hele EduNumbers. Du skal ikke bare konvertere tall; du skal bruke representasjon, byteorden, signedness, bitfelt, tekst og struktur for å forklare en ukjent buffer.
Du får denne syntetiske hex-dumpen:
0000: 45 4E 55 4D 01 A5 10 00 34 12 FE FF 50 4C 4F 4F
0010: 53 00 00 00 78 56 34 12
Du får bare tre sikre opplysninger:
Resten skal utledes og begrunnes.
Se på dumpen uten å regne først.
Hvilke områder ser ut som tekst? Hvilke byte kan være flagg? Ser du verdier som kan være little-endian heltall?
Skriv hypotesene før du leser videre.
Dumpen har 24 byte, med offsets 0x00 til
0x17.
Marker hvert byte med offset. Dette gjør senere hypoteser presise.
De første fire bytene er:
45 4E 55 4D
Som ASCII blir dette ENUM.
Det er en sterk kandidat til en magic/signatur, men merk ordet kandidat: mønsteret alene er ikke matematisk bevis.
Ved offset 0x04 finner vi 01. Det kan passe
som en versjon.
Ved 0x05 finner vi A5:
1010 0101₂
Hvis dette er et flaggfelt, er bit 7, 5, 2 og 0 satt.
Offset 0x06–0x07 inneholder:
10 00
Som little-endian 16-bit blir dette 0x0010 = 16.
Som big-endian blir det 0x1000 = 4096.
Siden bufferen bare er 24 byte, kan 16 være en plausibel lengde for alle 16 byte etter den åtte byte lange headeren — men plausibilitet er fortsatt ikke det samme som dokumentasjon.
Offset 0x08–0x09:
34 12
Little-endian gir 0x1234 = 4660.
Offset 0x0A–0x0B:
FE FF
Little-endian unsigned gir 65534. Tolket som signed
16-bit toerkomplement blir samme bitmønster −2.
Dette demonstrerer hvorfor signedness må være del av formatbeskrivelsen.
Offset 0x0C–0x13 er:
50 4C 4F 4F 53 00 00 00
ASCII gir PLOOS etterfulgt av tre nullbyte. En mulig
modell er et fast 8-byte tekstfelt med null-padding.
De siste fire bytene:
78 56 34 12
Little-endian gir:
0x12345678
Nå har vi flere felt som passer samme byteorden. Det styrker hypotesen om little-endian format.
En konsistent modell er:
| Offset | Størrelse | Mulig felt | Tolkning |
|---|---|---|---|
| 0x00 | 4 | magic | ASCII ENUM |
| 0x04 | 1 | version | 1 |
| 0x05 | 1 | flags | 0xA5 |
| 0x06 | 2 | length | 16, little-endian |
| 0x08 | 2 | id | 0x1234 |
| 0x0A | 2 | delta | −2 signed |
| 0x0C | 8 | name | PLOOS, null-padded |
| 0x14 | 4 | value | 0x12345678 |
Denne strukturen er fasiten for den syntetiske oppgaven. I ekte reverse engineering ville vi krevd flere observasjoner eller dokumentasjon før vi kalte modellen sikker.
I Python kan deler av bufferen undersøkes eksplisitt:
data = bytes.fromhex(
"45 4E 55 4D 01 A5 10 00 34 12 FE FF "
"50 4C 4F 4F 53 00 00 00 78 56 34 12"
)
length = int.from_bytes(data[6:8], "little")
ident = int.from_bytes(data[8:10], "little")
delta = int.from_bytes(data[10:12], "little", signed=True)
value = int.from_bytes(data[20:24], "little")
print(length, ident, delta, hex(value))Forventet resultat er 16 4660 −2 0x12345678.
Ingen av de viktigste bytefølgene endret seg under analysen. Det som endret seg var tolkningen vår.
Kursets hovedidé er nettopp dette: tall og byte er verdier og bitmønstre; base, signedness, byteorden, tekstkoding og feltstruktur er representasjonsregler som gir mønstrene mening.
Lag en kort analyserapport som inneholder:
0xA5FE FFNår du kan forklare hvorfor hver tolkning er rimelig, har du brukt hele EduNumbers som et verktøy.