Számábrázolás a számítógépen
A számítógépben a adatokat binárisan, bitek sorozataként ábrázoljuk. A továbbiakban tételezzük fel, hogy az adatokat egy 'n' bites tárolóegységben, ún. regiszterben tároljuk. Mivel ebben egymástól függetlenül 'n' különböző bit tárolható, ez összesen m=2n különböző bitvariációt tesz lehetővé. Tehát egy 'n' bites regiszterben 'm' számú különböző (binárisan kódolt) adatot tárolhatunk. Az 'm' számot a regiszter modulusának is szokás nevezni.
Például
– egy 8 bites regiszter modulusa m=256;
– egy 16 bites regiszter modulusa m=65536;
– egy 24 bites regiszter modulusa m=16,777,216;
– egy 32 bites regiszter modulusa m=4,294,967,296.Egy 'n' bites regiszterben pontosan m=2n darab különböző binárisan kódolt adat tárolható.
Mivel egy regiszterben biteket tárolunk, egy 'n' bites regiszter egybites tárolóegységek sorozataként is felfogható. Ha bi a regiszter i-dik bitjét jelenti (amelynek értéke 0 vagy 1), akkor egy regisztert
bn−1bn−2...b2b1b0
formában írhatunk le, ahol bn−1 a legnagyobb sorszámú bit, b0 pedig a legkisebb sorszámú bit.Számok ábrázolása esetén (vagyis amikor a regiszterben tárolt bitsorozatot számként értelmezzük) a legmagasabb sorszámú bit rendszerint a szám előjelét adja meg:
bn−1=0 esetén a regiszterben tárolt szám pozitív;
bn−1=1 esetén a regiszterben tárolt szám negatív.
Ilyenkor a legmagasabb helyiértékű bitet előjelbitnek nevezzük.Ami egy binárisan ábrázolt szám regiszterbeli elhelyezését illeti, általános szabály, hogy a szám legkisebb helyiértékű bitjét (LSB) a lehető legkisebb sorszámú biten (pl. b0-ban), és ennek megfelelően a szám legnagyobb helyiértékű bitjét (MSB) a lehető legnagyobb sorszámú biten tároljuk.
Megjegyzések:
(1) A "legkisebb sorszámú" és "legnagyobb sorszámú" úgy értendő, hogy a szám ábrázolása mindig pontosan meghatározza, melyek a regiszterben azok a bitek, amelyek a szám számjegyeit tárolják.
(2) Egy több bájton ábrázolt szám bájt szervezésű memóriában történő tárolásakor az egyes bájtok (vagy memóriarekeszek) elhelyezésére rendszerint ugyanezt az elvet követjük, vagyis a kisebb helyiértékű számjegyeket tartalmazó ("kisebb helyiértékű") bájtok memóriacíme mindig kisebb. Az ábrázolt szám, pontosabban a számot tároló bájtok memóriacíme mindig a számhoz tartozó legkisebb című memóriarekesz címe lesz.
(3) Egy több bájton ábrázolt szám fájlba írásakor azonban a bájtok sorrendje legtöbbször (pl. a Java DataOutputStream szűrőjét használva) megfordul, azaz a legnagyobb helyiértékű bájt lesz az első, és a legkisebb helyiértékű bájt az utolsó ("big-endian order").Ha egy regiszterben számokat tárolunk, a számokat különböző módon feleltethetjük meg a regiszter bitjeinek. Ennek megfelelően a következő kódolási vagy számábrázolási módokról beszélhetünk:
- direkt kódolás⇒
- kettes komplemens kódolás⇒
- binárisan kódolt decimális (BCD) számábrázolás⇒
- lebegőpontos számábrázolás⇒
(1) direkt kódolás
Ha egy regiszterben egy bináris számrendszerben ábrázolt nemnegatív egész számot tárolunk úgy, hogy a regiszter bitjeinek a szám bináris számjegyeit feleltetjük meg a helyi értékek megszokott sorrendjében (azaz az LSB-hez a legkisebb, 1-es helyiérték tartozik, majd (jobbról balra haladva) a következő bithez a 2-es helyiérték, utána a 4-es, 8-as stb.), akkor ún. direkt kódolásról (vagy egyenes kódolásról) beszélünk. Mivel direkt kódolás esetén csak nemnegatív számokat ábrázolunk, ezért nincs szükségünk előjelbitre.
Direkt kódolás esetén például egy 8 bites regiszterben tárolható legkisebb szám 0000|00002=010, a legnagyobb pedig 1111|11112=25510.
(2) kettes komplemens kódolás
Tároljunk egy 'n' bites (m=2n modulusú) regiszterben egész számokat a következőképpen:
- az előjelbit értéke nemnegatív számok esetén legyen 0, negatív számok esetén pedig 1 (így m/2 nemnegatív és m/2 negatív egész számot tudunk ábrázolni);
- ábrázoljuk a nemnegatív számokat direkt kódban;
- ábrázoljuk a negatív számokat úgy, hogy
- képezzük a negatív számok abszolút értékét direkt kódban,
- komplementáljuk az így kapott számot, azaz írjunk 0 helyett 1-et és 1 helyett 0-t (ez az ún. egyes komplemens kódolás, amit rendszerint "felülhúzással" jelölünk), és
- a komplementált számhoz adjunk hozzá binárisan 1-et.
Ezt a kódolást kettes komplemens kódolásnak nevezzük.
Ábrázoljunk példaként egy 8 bites regiszterben kettes komplemens kódban néhány számot (a bitsorozatok után alsó indexben megadott '2' most kettes komplemens kódban ábrázolt számot jelöl):
decimális érték kettes komplemens kód decimális érték kettes komplemens kód 010 0000|00002 110 0000|00012 −110 1111|11112 210 0000|00102 −210 1111|11102 310 0000|00112 −310 1111|11012 ... 2310 0001|01112 −2310 1110|10012 ... 12610 0111|11102 −12610 1000|00102 12710 0111|11112 −12710 1000|00012 −12810 1000|00002 Például −2310=1110|10012 mivel 0001|01112=1110|10002 (egyes komplemens) és ehhez 1-et binárisan hozzáadva 1110|10002+12=1110|10012 adódik.
Jegyezzük meg, hogy kettes komplemens kódban 12710 a legnagyobb ábrázolható pozitív szám, és −12810 a legkisebb ábrázolható negatív szám.Ha képezzük a −2310+2310 összeget 0001|01112+1110|10012=1|0000|00002 adódik, ami éppen a 8 bites regiszter m=256 modulusának 2-es számrendszerben ábrázolt értéke. Általánosan is igaz, hogy egy 'n' bites regiszterben (a megengedett −m/2≤x<m/2 számtartományon belül) egy kettes komplemens kódban ábrázolt negatív szám és a vele abszolút értékben megegyező nemnegatív szám összege a regiszter modulusát adja. Formálisan kifejezve: ha a megengedett számtartományon belül 'v' egy nemnegatív egész számot jelöl, 'w' pedig ennek kettes komplemens kódját, akkor v+w=m vagy másképpen kifejezve v+w=0 (mod m) teljesül.
Két fontos szabály:
- Egy kettes komplemens kódban ábrázolt negatív szám abszolút értékét ugyanazzal az algoritmussal kaphatjuk meg, mint ahogy egy negatív szám kettes komplemens kódját képeztük (azaz komplementálással és 1 bináris hozzáadásával).
- Például w=1010|01102 esetén v=0101|10012+12=0101|10102 vagyis |w|=9010
- Egy nemnegatív szám kivonását elvégezhetjük a szám kettes komplemens kódjának a hozzáadásával. A szabály alkalmazásakor a legmagasabb helyiértékű számjegyek összeadása után keletkező átvitelt figyelmen kívül kell hagyni.
- Például számoljuk ki az x=4910−2710 különbséget.
Legyen v=2710=0001|10112, ekkor w=−2710=1110|01002+12=1110|01012. Mivel 4910=0011|00012 ezért a keresett különbség
x=4910−2710=0011|00012+1110|01012=1|0001|01102
elhagyva a legmagasabb helyiértéken keletkező 1 bitet (az átvitelt), a különbségre a helyes x=0001|01102=2210 érték adódik.Negatív egész számokat kettes komplemens kódban ábrázolva a kivonást összeadásra tudjuk visszavezetni.Ennek megfelelően kétféle túlcsordulás fordulhat elő:
- ha az összeadás eredménye túl nagy pozitív szám; pl. 127+110=12810, de 0111|1111+0000|00012= 1000|00002= −12810≠ 12810 (az előjelbitek jelzik, hogy két pozitív számot adtunk össze, de az összeg előjelbitje negatív lett)
- ha az összeadás eredménye túl kicsi negatív szám; pl. −128−110=−12910, de 1000|0000+1111|11112= 1|0111|11112= 12710≠ −12910 (az előjelbitek jelzik, hogy két negatív számot adtunk össze, de az összeg előjelbitje pozitív lett)
- az általános szabály a következőképpen fejezhető ki: ha a C=A+B összeget számítjuk ki kettes komplemens kódban ábrázolt egész számok esetén, és az előjelbiteket EA, EB és EC módon jelöljük, akkor az
OV = EA∧EB∧⌝EC ∨ ⌝EA∧⌝EB∧EC
logikai kifejezés akkor és csak akkor igaz, ha az összeadás során túlcsordulás történt
A fenti algoritmusokat meg valósító JavaScript programok:
(1) Decimális szám kettes komplemens kódban ábrázolt bináris számmá alakítása:
// decimális szám átalakítása kettes komplemens kódban ábrázolt bináris számmá (8 biten) function dec2bin(x) { var b=""; var q=1; while(q>0) { q=Math.trunc(x/2); // egész osztás!! if(x%2==1) { b="1"+b; } else { b="0"+b; } x=q; } while(b.length<m) { b="0"+b; // vezető 0 hozzáadása } return b; } function bin2binc(x) { var xc=""; for(var i=0;i<x.length;i++) { if(x[i]=="1") { xc=xc+"0"; } else { xc=xc+"1"; } } return xc; } function inc(x) { var xc=""; var j=x.length-1; // utolsó karakter indexe var atvitel=1; while(j>=0) { if(x[j]=="0" && atvitel==0) { xc="0"+xc; } else if(x[j]=="0" && atvitel==1) { xc="1"+xc; atvitel=0; } else if(x[j]=="1" && atvitel==0) { xc="1"+xc; } else { xc="0"+xc; atvitel=1; } j=j-1; } return xc; } /* feltétel: nyolc biten ábrázoljuk a számokat (m=8), tehát -128<=xd<127 teljesül */ var m=8; // bitek száma var xd=-127; var xb=""; writeln("Decimális szám: "+xd); writeln(); if(xd>=0) { xb=dec2bin(xd); } else { var xa,x; xd=-xd; writeln("Abszolút érték: "+xd); writeln(); xa=dec2bin(xd); writeln("Bináris érték: "+xa); x=bin2binc(xa); writeln("Egyes komplemens: "+x); xb=inc(x); writeln(); } writeln("Kettes komplemens: "+xb); writeln("__________");
(2) Kettes komplemens kódban adott bináris szám decimálissá alakítása:
// kettes komplemens kódban adott (8 bites) bináris szám átalakítása decimális számmá function bin2dec(x) { var d=0; var helyiertek=1; for(var i=1;i<x.length;i++) { helyiertek*=2; } for(var i=0;i<x.length;i++) { var sz; if(x[i]=="1") { sz=1; } else { sz=0; } var r=sz*helyiertek; d+=r; helyiertek/=2; } return d; } function bin2binc(x) { var xc=""; for(var i=0;i<x.length;i++) { if(x[i]=="1") { xc=xc+"0"; } else { xc=xc+"1"; } } return xc; } function inc(x) { var xc=""; var j=x.length-1; // utolsó karakter indexe var atvitel=1; while(j>=0) { if(x[j]=="0" && atvitel==0) { xc="0"+xc; } else if(x[j]=="0" && atvitel==1) { xc="1"+xc; atvitel=0; } else if(x[j]=="1" && atvitel==0) { xc="1"+xc; } else { xc="0"+xc; atvitel=1; } j=j-1; } return xc; } /* feltétel: nyolc biten ábrázoljuk a számokat (m=8), tehát xb.length=8 teljesül */ var xb="10000001"; var s; writeln("Bináris szám: "+xb); writeln(); if(xb[0]==0) { // előjelbit pozitív s="+"; } else { // előjelbit negatív s="-"; var x; x=bin2binc(xb); writeln("Egyes komplemens: "+x); xb=inc(x); writeln("Kettes komplemens: "+xb); writeln(); } writeln("Decimális szám: "+s+bin2dec(xb)); writeln("__________");
(3) binárisan kódolt decimális (BCD) számábrázolás
BCD számábrázolás esetén tízes számrendszerben megadott egész számokat kódolunk a decimális számjegyek adott számú biten történő bináris megadásával. A BCD számábrázolás két típusa:
– pakolt, csomagolt vagy tömörített ("packed") BCD esetén a decimális számjegyeket 4 biten (1 tetrádon) ábrázoljuk (egy bájt felső vagy alsó 4 bitje egy ún. fél bájt vagy tetrád, angolul "nibble");
– pakolatlan, kibontott vagy zónázott ("unpacked") BCD esetén a decimális számjegyeket 8 biten (1 bájton) ábrázoljuk (a felső 4 bitet az ún. zónabitek alkotják, az alsó 4 bit pedig az ábrázolandó decimális számjegy bináris alakját tartalmazza).
- BCD számábrázolás esetén a decimális számjegyeket helyiértékük szerint balról jobbra helyezzük el a számjegyek számára rendelkezésre álló bájtsorozatban.
- Pakolt BCD számábrázolás esetén egy bájton két számjegyet ábrázolunk (a magasabb helyiértékű számjegyet a "felső" 4 biten, az alacsonyabb helyiértékű számjegyet az "alsó" 4 biten tárolva). (Megjegyzés: az utolsó bájt alsó 4 bitjét a szám előjelének az ábrázolására is használhatjuk.)
Mivel így csak páros számú számjegyet tudunk ábrázolni, attól függően, hogy előjeles vagy előjel nélküli számot ábrázolunk, a következőképpen járhatunk el:
- előjel nélküli ábrázolás esetén: ha a decimális számjegyek száma nem páros, akkor a számjegyeket kiegészítjük egy vezető 0-val;
- előjeles számábrázolás esetén: ha a decimális számjegyek száma nem páratlan, akkor a számjegyeket kiegészítjük egy vezető 0-val.
- Pakolatlan BCD számábrázolás esetén egy bájton egy számjegyet ábrázolunk:
– a "felső" 4 biten (az ún. zónabiteken) egy előre rögzített bitsorozatot tárolunk (ez lehet pl. 0000 vagy FFFF);
– az "alsó" 4 biten pedig egy decimális számjegyet tárolunk bináris alakban.- Ha a számnak van előjele, az pakolt BCD kódolás esetén a számjegyek utáni, jobb szélső 4 biten vagy tetrádon helyezkedik el, pakolatlan BCD kódolás esetén pedig az utolsó számjegyet tartalmazó bájt "felső" 4 bitjén, az utolsó bájt zónabitjei helyett.
Ha az előjelet négy biten ábrázoljuk,
- az A, C, E (egyes alkalmazások esetén az F) hexadecimális számjegyek a '+' előjelet kódolják;
- a B vagy D számjegyek pedig a '−' előjelet kódolják.
- Ha a memóriában tároljuk a számot, akkor a legmagasabb helyiértékű számjegy(ek) lesznek a legalacsonyabb memóriacímen ("big-endian order"). (Ezt a tárolási sorrendet követjük egy karakterlánc (string) karaktereinek, vagy egy tömb elemeinek a memóriában való tárolásakor.)
Az x86-os architektúra numerikus adatfeldolgozó processzora (NDP) támogatja az előjeles pakolt BCD formátumot. Az így ábrázolt adatok rögzített méretűek (10 bájt hosszúak):
– a jobb szélső 9 bájt tartalmazza a decimális számjegyeket (18 számjegy);
– a bal szélső bájt bal szélső bitje előjelbit: a bit 0 értéke esetén a szám pozitív, 1 értéke esetén a szám negatív (a fennmaradó 7 bit kihasználatlan).Felhasználva a korábban létrehozott⇒ 'dec2bin4' függvényt, a pakolt, előjel nélküli BCD számábrázolást megvalósító algoritmust például a következőképpen írhatjuk le egy JS program segítségével:
// decimális szám átalakítása előjel nélküli pakolt BCD formátumra function dec2bin4(x) { var b=""; var q,m; while(x>0) { if(x%2==1) { m="1"; x=x-1; } else { m="0"; } q=x/2; b=m+b; x=q; } var i=b.length; while(i<4) { b="0"+b; // vezető 0 hozzáadása i=i+1; } return b; } var x="314"; writeln("Decimális szám: "+x); writeln(); var i=0; var bcd=""; while(i<x.length) { bcd=bcd+dec2bin4(x[i])+" "; i=i+1; } if(x.length%2!=0) { bcd="0000"+" "+bcd; } writeln("Pakolt előjel nélküli BCD kód: "); writeln(bcd); writeln("__________");
Az előjeles BCD számábrázolást megvalósító algoritmust hasonlóképpen kódolhatjuk:
// decimális szám átalakítása előjeles pakolt BCD formátumra function dec2bin4(x) { var b=""; var q,m; while(x>0) { if(x%2==1) { m="1"; x=x-1; } else { m="0"; } q=x/2; b=m+b; x=q; } var i=b.length; while(i<4) { b="0"+b; // vezető 0 hozzáadása i=i+1; } return b; } var x="-314"; var sign=0; // alapértelmezésben pozitív if (x[0]=="-"){ sign=1; }; writeln("Decimális szám: "+x); writeln(); var i=0; var bcd=""; while(i<x.length) { bcd=bcd+dec2bin4(x[i])+" "; i=i+1; } if(x.length%2==0) { bcd="0000"+" "+bcd; } if(sign==0) { bcd=bcd+"1100"; // hexa 'C' jelzi a pozitív előjelet } else { bcd=bcd+"1101"; // hexa 'D' jelzi a negatív előjelet } writeln("Pakolt előjeles BCD kód: "); writeln(bcd); writeln("__________");
Az előjel nélküli pakolatlan ("zónázott") BCD számábrázolást megvalósító algoritmus:
// decimális szám átalakítása előjel nélküli pakolatlan BCD formátumra function dec2bin4(x) { var b=""; var q,m; while(x>0) { if(x%2==1) { m="1"; x=x-1; } else { m="0"; } q=x/2; b=m+b; x=q; } var i=b.length; while(i<4) { b="0"+b; // vezető 0 hozzáadása i=i+1; } return b; } var x="314"; writeln("Decimális szám: "+x); writeln(); var i=0; var bcd=""; var zona="1111"; while(i<x.length) { bcd=bcd+zona+dec2bin4(x[i])+" "; i=i+1; } writeln("Pakolatlan előjel nélküli BCD kód: "); writeln(bcd); writeln("__________");
Az előjeles pakolatlan ("zónázott") BCD számábrázolást megvalósító algoritmus pedig a következő:
// decimális szám átalakítása előjeles pakolatlan BCD formátumra function dec2bin4(x) { var b=""; var q,m; while(x>0) { if(x%2==1) { m="1"; x=x-1; } else { m="0"; } q=x/2; b=m+b; x=q; } var i=b.length; while(i<4) { b="0"+b; // vezető 0 hozzáadása i=i+1; } return b; } var x="314"; var sign=0; // alapértelmezésben pozitív if (x[0]=="-"){ sign="1"; }; writeln("Decimális szám: "+x); writeln(); var i=0; var bcd=""; var zona="1111"; while(i<x.length) { if(i==x.length-1) { // utolsó bájt if(sign==0) { zona="1010"; // hexa 'C' jelzi a pozitív előjelet } else { zona="1011"; // hexa 'D' jelzi a negatív előjelet } } bcd=bcd+zona+dec2bin4(x[i])+" "; i=i+1; } writeln("Pakolatlan előjeles BCD kód: "); writeln(bcd); writeln("__________");
Számábrázolás a számítógépen: lebegőpontos számábrázolás
Lebegőpontos számábrázolás esetén 2-es normál alakban megadott valós számokat kódolunk adott pontossággal. Ennek alapja az, hogy az ábrázolandó 'x' valós számot
x = s*m*2k
normál alakban adjuk meg, ahol 's' a szám előjele (s=±1), 'm' a kettedes tört formában megadott ún. mantissza (m∈ℝ), és 'k' az ún. karakterisztika (k∈ℤ).A mantissza számjegyeinek (bitjeinek) a száma a számábrázolás pontosságát, a karakterisztika pedig az ábrázolható számok nagyságrendjét határozza meg (vö. Nyakóné Juhász 2011: 21).
A lebegőpontos számok ábrázolása
- egyszeres pontosságú valós számok esetén 32 biten,
- duplapontosságú valós számok esetén 64 biten történik.
A továbbiakban az egyszeres pontosságú, 32 bites lebegőpontos számábrázolással foglalkozunk. (Az Institute of Electrical and Electronics Engineers (IEEE) által a nyolcvanas években kiadott IEEE 754 nevű szabvány alapján, vö. Nyakóné Juhász 2011: 21-22).
Ha egyszeres pontossággal, 32 biten ábrázolunk egy lebegőpontos számot, akkor
– az előjelet a bal szélső 1 biten a szokásos módon,⇒
– a karakterisztikát a következő 8 biten többletes kódolással,
– a mantisszát pedig a fennmaradó 23 biten fixpontos kódolással
ábrázoljuk.
b b b ... b b b b b b b ... b b b előjel (sign, 1 bit) karakterisztika (exponent, 8 bit) mantissza (fraction, 23 bit) Jelöljük
– az ábrázolt valós számot 'x'-szel;
– a karakterisztika tényleges értékét 'k'-val, az egyszeres pontosságú lebegőpontos számnak a karakterisztika számára fenntartott 8 bitjén direkt kódolással ábrázolt "többletes" (127-tel, ill. kis számok esetén 126-tal eltolt) értéket pedig exp-val;
– a mantissza tényleges értékét 'm'-mel, az egyszeres pontosságú lebegőpontos számnak a mantissza számára fenntartott 23 bitjén fixpontos kódolással ábrázolt értéket pedig frac-val.Három esetet különböztetünk meg:
(1) "normál" eset: a karakterisztika legalább −126, és az ábrázolt exponens legalább 1 (vagyis nem zérus)
Normál esetben az ábrázolandó 'x' valós számra
|x|≥2−126≈1.1754943508222875079687365372222*10−38
teljesül.Ha az ábrázolandó valós számra 2−126≤|x|<2128 teljesül, akkor
- a karakterisztika tényleges értékére −126≤k≤127 teljesül;
- a karakterisztika (direkt kódban) ábrázolt értékére 1≤exp≤254 teljesül;
- 'k' és 'exp' között a kapcsolatot
exp=k+127, ill. k=exp−127
módon fejezhetjük ki;- a mantissza tényleges értékére 1≤m<2 teljesül;
- a mantissza ábrázolt értékét kettedes tört formában frac=0.b1...b23 módon fejezhetjük ki, ahol bi a mantissza 2−i-dik helyiértékén álló bináris számjegy (1≤i≤23);
- ha a mantisszát kettedes törtben ábrázoljuk, 'm' és 'frac' között a kapcsolatot
m=1+frac, ill. m=1.b1...b23
módon fejezhetjük ki.Ha a karakterisztika (k) kódolt értéke nem zérus (vagyis a karakterisztika −126-nál nem kisebb egész szám), a mantissza tényleges értékét (m) egy olyan kettedes törttel fejezzük ki, amelynek egész része mindig 1 (vagyis a mantissza 1≤m<2 közötti valós szám). Az egyszeres pontosságú lebegőpontos számnak a mantissza számára fenntartott 23 bitje ilyenkor a mantissza tört részének bináris számjegyeit adja meg.Megjegyzések:
– a karakterisztika tényleges értékének (k) fenti módon történő kódolását ún. 127-es többletes kódolásnak nevezzük (ez k+127 direkt kódban történő ábrázolását jelenti adott számú (ti. 8) biten);
– a kettedestörtek számjegyeinek (az egész résznek és/vagy a tört résznek) adott számú biten történő ábrázolását nevezzük ún. fixpontos számábrázolásnak (a mantissza ábrázolása ezt a kódolási formát követi, mivel a mantissza tört részének bináris számjegyeit rögzített számú (ti. 23) biten adjuk meg);
– az (1) esetben ábrázolható legkisebb szám esetén exp=1 és frac=0, tehát
x(1),min=1.0000...*2−126=2−126;
– az (1) eset ekvivalens azzal, amikor a karakterisztika −125≤k≤128 közötti egész szám és a mantissza 1/2≤m<1 közötti valós szám (vagyis a mantisszát 0.1-re normáljuk). Ekkor a karakterisztikát 8 biten 126-os többletes kódban adjuk meg, azaz az exp=k+126 egész számot ábrázoljuk direkt kódban (1≤exp≤254), és a mantissza kettedestört alakjában a mantissza 0.1 utáni bináris számjegyeit ábrázoljuk a rendelkezésre álló 23 biten.(2) a zérushoz közeli ("kis") számok esete: a karakterisztika −126, és az ábrázolt exponens zérus
Zérushoz közeli számok esetén az ábrázolandó 'x' valós számra
|x|<2−126≈1.1754943508222875079687365372222*10−38
teljesül.Ha az ábrázolandó valós számra 0≤|x|<2−126 teljesül, akkor
- a karakterisztika tényleges értéke k=−126 (és nem −127, mert ebben az esetben más kódolást használunk, mint "normál" esetben!);
- a karakterisztika (direkt kódban) ábrázolt értéke exp=0 (ez az előjelbit után egy nyolc bites 00000000 bitsorozatot jelent);
- 'k' és 'exp' között a kapcsolatot
exp=k+126, ill. k=exp−126
módon fejezhetjük ki (bár erre valójában most nincs is szükség, mert mind 'k', mind 'exp' értéke rögzített);- a mantissza tényleges értékére 0≤m<1 teljesül;
- a mantissza ábrázolt értékét kettedes tört formában frac=0.b1...b23 módon fejezhetjük ki, ahol bi a mantissza 2−i-dik helyiértékén álló bináris számjegy (1≤i≤23);
- ha a mantisszát kettedes törtben ábrázoljuk, 'm' és 'frac' között a kapcsolatot
m=0+frac, ill. m=0.b1...b23
módon fejezhetjük ki (a mantissza kettedestört alakjában a mantissza 0. utáni bináris számjegyeit ábrázoljuk a mantissza számára rendelkezésre álló 23 biten).Ha a karakterisztika (k) kódolt értéke zérus (vagyis a karakterisztika értéke −126), a mantissza tényleges értékét (m) egy olyan kettedes törttel fejezzük ki, amelynek egész része mindig 0 (vagyis a mantissza 0≤m<1/2 közötti valós szám). Az egyszeres pontosságú lebegőpontos számnak a mantissza számára fenntartott 23 bitje ilyenkor is a mantissza tört részének bináris számjegyeit adja meg. Ha a mantissza értéke zérus, akkor a lebegőpontos szám értéke is zérus (az előjeltől függetlenül).Megjegyzések:
– ha az (1) esetben a mantisszát 0.1-re normáljuk (azaz 1/2≤m<1 teljesül), akkor a (2) eset annak a speciális esetnek felel meg, amikor a karakterisztika k=−126 (és exp=0); de mivel szeretnénk a zérust is ábrázolni, most megengedjük az 1/2-nél kisebb nemnegatív mantisszákat is (vagyis ebben az esetben 0≤m<1 teljesül);
– a (2) esetben alkalmazott kódolás lehetővé teszi a zérus ábrázolását ("lebegőpontos nulla"), sőt valójában ez kétféleképpen is lehetséges: ha az egyszeres pontosságú lebegőpontos számok számára rendelkezésre álló 32 biten csupa 0 szerepel, "pozitív" zérusról, ha pedig az első bit 1, és utána 31 biten csupa 0 szerepel, "negatív" zérusról beszélhetünk;
– a (2) esetben ábrázolható legnagyobb szám m=0.1111... és exp=0 (azaz k=−126) miatt
x(2),max=0.1111...*2−126≲2−126.(3) túlcsordulás: a karakterisztika legalább 128, és az ábrázolt exponens 255 (azaz binárisan 1111|1111) (az abszolút értékben "túlságosan" nagy, ezért lebagőpontosan már nem ábrázolható számok esete)
Ha az ábrázolandó számra 2128≤|x| teljesül, akkor a valós szám már nem ábrázolható az egyszeres pontosságú lebegőpontos formátumban. Ekkor
- a karakterisztika tényleges értékére 128≤k teljesül (feltéve, hogy az (1) esethez hasonlóan a mantissza most is az 1≤m<2 tartományba esik);
- a karakterisztika (direkt kódban) ábrázolt értékére exp=255 teljesül (azaz a karakterisztika 8 biten ábrázolt alakja a 11111111 bitsorozat);
- a mantissza tényleges értéke és ábrázolt értéke tetszőleges lehet (ebben az esetben nem vesszük figyelembe).
Túlcsorduláskor az ábrázolt bitsorozatot a mantissza értékétől függetlenül nem számként értelmezzük, hanem az előjelbittől függően plusz, ill. minusz végtelenként (±∞). A túlcsordulás szokásos jelölése NaN ("not a number").
1. példa (vö. Nyakóné Juhász 2011: 22): Tekintsük az alábbi 32 bites bitsorozatot, amely egy egyszeres pontosságú lebegőpontos számot határoz meg:
x=1 10000111 101000000000000000000002
Határozzuk meg, ez milyen valós számnak felel meg!A lebegőpontos számábrázolás (1) esetét alapul véve
– mivel az előjelbit 1, ezért 'x' negatív;
– mivel k'=1000|01112=13510, ezért a karakterisztika értéke k=k'−127=8;
– mivel az 1-re normált mantissza m=1.101000...2, ezért
m=1+1/2+1/8=8/8+5/8=13/8Tehát a keresett valós szám 28=256 miatt
x=−13/8*28=−13/8*256=−416Vizsgáljuk meg azt az esetet, amikor a mantisszát 0.1-re normáljuk. Ekkor a következőket kapjuk:
– mivel az előjelbit 1, ezért 'x' negatív;
– mivel k'=1000|01112=13510, ezért a karakterisztika értéke k=k'−126=9;
– mivel a 0.1-re normált mantissza m=0.1101000...2, ezért
m=1/2+1/4+1/16=8/16+4/16+1/16=13/16
Tehát a keresett valós szám 29=512 miatt
x=−13/16*29=−13/16*512=−416
megegyezik az előző értékkel.
Az algoritmus egyszerű megvalósításához fel fogjuk használni a JavaScript nyelv Math.pow(x,k) függvényét, amely egy 'x' szám 'k'-dik hatványát (azaz xk értékét) számolja ki. A fenti algoritmust megvalósító JavaScript program ezek után a következő:
// lebegőpontos számot ábrázoló bináris számsorozat átalakítása decimális számmá (1. példa) function bin2dec(x) { var d=0; var helyiertek=1; for(var i=1;i<x.length;i++) { helyiertek*=2; } for(var i=0;i<x.length;i++) { var sz; if(x[i]=="1") { sz=1; } else { sz=0; } var r=sz*helyiertek; d+=r; helyiertek/=2; } return d; } function binf2decf(x) { var d=0; var helyiertek=1/2; for(var i=0;i<x.length;i++) { var sz; if(x[i]=="1") { sz=1; } else { sz=0; } var r=sz*helyiertek; d+=r; helyiertek/=2; } return d; } /* feltétel: exp>0 vagyis a lebegőpontos számábrázolás (1) esetének megfelelő algoritmust valósítjuk meg */ var s="1"; var exp="10000111"; var frc="10100000000000000000000"; writeln("Lebegőpontos szám: "+s+" "+exp+" "+frc); writeln(); var e; var k; var m; if(s=="0") { e=1; } else { e=-1; } k=bin2dec(exp)-127; m=1+binf2decf(frc); writeln("Előjel: "+e); writeln("Karakterisztika: "+k); writeln("Mantissza: "+m); writeln(); var d=e*Math.pow(2,k)*m; writeln("Decimális szám: "+d); writeln("__________");
2. példa: Tekintsük az alábbi 32 bites bitsorozatot, amely most is egy egyszeres pontosságú lebegőpontos számot határoz meg:
x=0 00000000 101000000000000000000002
Határozzuk meg, ez (közelítőleg) milyen valós számnak felel meg!A lebegőpontos számábrázolás (2) esetét alapul véve
– mivel az előjelbit 0, ezért 'x' pozitív;
– mivel k'=0000|00002=010, ezért a karakterisztika értéke k=k'−126=−126;
– mivel a 0.-val kezdődő mantissza m=0.101000...2, ezért
m=1/2+1/8=5/8Tehát a keresett valós szám
x=5/8*2−126≈7.3468...10−39
A fenti algoritmust megvalósító JavaScript program:
// lebegőpontos számot ábrázoló bináris számsorozat átalakítása decimális számmá (2. példa) function binf2decf(x) { var d=0; var helyiertek=1/2; for(var i=0;i<x.length;i++) { var sz; if(x[i]=="1") { sz=1; } else { sz=0; } var r=sz*helyiertek; d+=r; helyiertek/=2; } return d; } /* feltétel: exp=0 vagyis a lebegőpontos számábrázolás (2) esetének megfelelő algoritmust valósítjuk meg */ var s="0"; var exp="00000000"; var frc="10100000000000000000000"; writeln("Lebegőpontos szám: "+s+" "+exp+" "+frc); writeln(); var e; var k; var m; if(s=="0") { e=1; } else { e=-1; } k=-126; m=binf2decf(frc); writeln("Előjel: "+e); writeln("Karakterisztika: "+k); writeln("Mantissza: "+m); writeln(); var d=e*Math.pow(2,k)*m; writeln("Decimális szám: "+d); writeln("__________");
3. példa: Tekintsük az alábbi valós számot:
x=7.510
Határozzuk meg, a szám lebegőpontos ábrázolása milyen bitekből áll!A szám lebegőpontos ábrázolása x≫2−126 miatt egészen nyilvánvalóan a normál esetnek felel meg.⇒
- a szám pozitív, tehát előjelbitje s=0
- határozzuk meg az x=7.5 szám 2-es normál alakját
- osszuk el x=7.5-öt addig 2-vel, amíg 1 és 2 közötti számot nem kapunk (megjegyzés: ha 'x' kisebb lenne 1-nél, akkor nem osztanunk, hanem szoroznunk kellene 2-vel)
- mivel x/4=7.5/4=1.875 és 4=22, a keresett normál alak x=1.875*22
- mivel k=2, a karakterisztika ábrázolandó bitjeit exp=k+127=129 módon kapjuk meg; ezt kettes számrendszerben ábrázolva exp=12910=1000|00012 adódik
- mivel m=1.875, a mantissza ábrázolandó bitjeit a frac=0.875 tizedes tört kettedes törtté alakításával kapjuk meg; egy kis számolás után⇒ frac=0.87510=0.1112 adódik
A fentiek alapján x=7.5 lebegőpontosan ábrázolt alakja
x= 0 10000001 11100000000000000000000 2
formában fejezhető ki.
A fenti algoritmust megvalósító JavaScript program:
// decimális szám átalakítása lebegőpontos számot ábrázoló bináris számsorozattá function dec2bin(x,maxi) { var b=""; var helyiertek=1; while(2*helyiertek<=x) { helyiertek*=2; } while(helyiertek>=1) { if(helyiertek<=x) { b+="1"; x-=helyiertek; } else { b+="0"; } helyiertek/=2; } while(b.length<maxi) { b="0"+b; // vezető 0 } return b; } function decf2binf(x,maxi) { var s=""; var i=1; while(x>0 && i<=maxi) { x*=2; if(x<1) { s+="0"; } else { s+="1"; x=x-1; } i++; } while(s.length<maxi) { s=s+"0" // záró 0 } return s; } /* feltétel: x abszolút értéke elegendően nagy (x>>0); a lebegőpontos számábrázolás normál (1) esetének megfelelő algoritmust valósítjuk meg */ var x=7.5; // var x=-0.15; writeln("Ábrázolandó decimális szám: "+x); writeln(); var s; var exp; var frc; var e; var k; var m; if(x>0) { e=1; s="0"; } else { e=-1; s="1"; x=-x; // abszolút érték } k=0; if(x>=2) { while(x>=2) { x=x/2; k=k+1; } } else if(x<1) { while(x<1) { x=x*2; k=k-1; } } exp=dec2bin(k+127,8); m=x; frc=decf2binf(m-1,23); writeln("Előjel: "+e); writeln("Karakterisztika: "+k); writeln("Mantissza: "+m); writeln(); // var d=e*Math.pow(2,k)*m; // writeln("Ábrázolandó decimális szám: "+d); writeln("Lebegőpontos szám: "+s+" "+exp+" "+frc); writeln("__________");