===== Negatív és lebegőpontos számok ábrázolása kettes számrendszerben ===== A számítógép memóriája biteket tárol, ezért a pozitív egész számok mellett a negatív és a törtrészt tartalmazó számokat is 0-k és 1-ek sorozatával kell ábrázolni. Ehhez előre meg kell határozni, hogy az egyes biteknek mi a jelentésük. Fontos, hogy ugyanaz a bitsorozat önmagában még nem határozza meg a tárolt szám értékét. A ''11111111'' bitsorozat jelenthet például: * előjel nélküli egész számként 255-öt; * 8 bites kettes komplemens előjeles számként −1-et; * egy hosszabb adat, karakter vagy utasítás részét. Az értelmezéshez tehát ismernünk kell az adattípust és a felhasznált ábrázolási módot. ==== 1. Előjel nélküli egész számok ==== Előjel nélküli ábrázolásnál minden bit a szám nagyságát adja meg. Egy 8 bites szám helyiértékei: ^ Helyiérték ^ 2⁷ ^ 2⁶ ^ 2⁵ ^ 2⁴ ^ 2³ ^ 2² ^ 2¹ ^ 2⁰ ^ | Érték | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Például: 00101101₂ = 32 + 8 + 4 + 1 = 45₁₀ ''n'' biten összesen ''2ⁿ'' különböző bitminta tárolható. Az előjel nélküli számok értéktartománya: 0 ... 2ⁿ − 1 8 bit esetén ez: 0 ... 255 ==== 2. Negatív egész számok ábrázolása ==== A negatív számok tárolására többféle módszert dolgoztak ki. A legismertebbek: * előjel–nagyságos ábrázolás; * egyes komplemens ábrázolás; * kettes komplemens ábrázolás. A modern számítógépek szinte mindig a //kettes komplemens// ábrázolást használják az előjeles egész számok tárolására. ==== 2.1. Előjel–nagyságos ábrázolás ==== Ebben a módszerben a legmagasabb helyiértékű, bal szélső bit az előjelet jelöli: * ''0'': pozitív szám; * ''1'': negatív szám. A többi bit a szám abszolút értékét tartalmazza. 8 biten például: +5 = 00000101 −5 = 10000101 A módszer könnyen értelmezhető, de kétféle nulla tartozik hozzá: +0 = 00000000 −0 = 10000000 Az összeadás és kivonás végrehajtása is körülményes, mert az előjelet külön kell kezelni. Emiatt egész számok tárolására ma már ritkán használják. ==== 2.2. Egyes komplemens ábrázolás ==== Egy negatív szám egyes komplemensét úgy kapjuk meg, hogy a megfelelő pozitív szám minden bitjét megfordítjuk: +5 = 00000101 −5 = 11111010 A bitenkénti megfordítás során minden 0-ból 1, minden 1-ből 0 lesz. Ennél a módszernél is kétféle nulla létezik: +0 = 00000000 −0 = 11111111 Ez a tulajdonság, valamint az aritmetikai műveletek bonyolultsága miatt az egyes komplemenst a mai általános célú számítógépek egész számok tárolására jellemzően nem használják. ==== 2.3. Kettes komplemens ábrázolás ==== A kettes komplemens ábrázolás előnye, hogy csak egyetlen nullaérték létezik, és ugyanaz az összeadó áramkör használható pozitív és negatív számok összeadására. Egy negatív szám kettes komplemens alakjának előállítása: - Írjuk fel a szám pozitív abszolút értékét a megadott bitszámon. - Fordítsuk meg az összes bitet. - Adjunk az eredményhez 1-et. Például a −5 előállítása 8 biten: 00000101 +5 bináris alakja 11111010 a bitek megfordítása + 1 ---------- 11111011 −5 kettes komplemens alakja Tehát: +5 = 00000101 −5 = 11111011 === Negatív kettes komplemens szám visszaalakítása === Ha egy kettes komplemens számban a bal szélső bit 1, akkor a szám negatív. Az abszolút érték meghatározásához: - fordítsuk meg az összes bitet; - adjunk hozzá 1-et; - az eredményt lássuk el negatív előjellel. Például: 11110110 ismeretlen negatív szám 00001001 a bitek megfordítása + 1 ---------- 00001010 10 Ezért: 11110110₂ = −10₁₀ Egy másik gyors módszer szerint a 8 bites negatív szám bitsorozatát először előjel nélküli számként értelmezzük, majd kivonjuk belőle ''2⁸ = 256'' értékét: 11110110₂ = 246 előjel nélküli számként 246 − 256 = −10 === A kettes komplemens értéktartománya === ''n'' bites kettes komplemens szám értéktartománya: −2ⁿ⁻¹ ... 2ⁿ⁻¹ − 1 Néhány gyakori eset: ^ Bitszám ^ Legkisebb érték ^ Legnagyobb érték ^ | 8 bit | −128 | 127 | | 16 bit | −32 768 | 32 767 | | 32 bit | −2 147 483 648 | 2 147 483 647 | | 64 bit | −2⁶³ | 2⁶³ − 1 | A tartomány azért nem szimmetrikus, mert a nulla is a nemnegatív bitminták egyikét foglalja el. 8 biten például: 01111111 = 127 00000000 = 0 11111111 = −1 10000000 = −128 ==== 2.4. Összeadás kettes komplemens számokkal ==== Kettes komplemens ábrázolásnál a pozitív és negatív számok a szokásos bináris összeadással összeadhatók. Számítsuk ki 8 biten az ''5 + (−3)'' műveletet: 00000101 +5 + 11111101 −3 ---------- 1 00000010 A kilencedik bitre kerülő átvitelt elhagyjuk, mert a számábrázolás csak 8 bites. Az eredmény: 00000010 = 2 Valóban: 5 + (−3) = 2 === Kivonás összeadással === Az ''A − B'' kivonás elvégezhető úgy, hogy ''A'' értékéhez hozzáadjuk ''B'' kettes komplemensét: A − B = A + (−B) Például ''7 − 5'': 00000111 +7 + 11111011 −5 ---------- 1 00000010 2 ==== 2.5. Túlcsordulás ==== Túlcsordulás akkor történik, ha a matematikai eredmény nem fér el a rendelkezésre álló bitszámon. 8 bites előjeles szám esetén a legnagyobb tárolható érték 127. Ha ehhez 1-et adunk: 01111111 127 + 00000001 1 ---------- 10000000 −128-ként értelmezhető bitminta A matematikai eredmény 128 lenne, de ez 8 bites előjeles számban nem ábrázolható. Előjeles összeadásnál túlcsordulás történt, ha: * két pozitív szám összege negatívnak látszik; vagy * két negatív szám összege pozitívnak látszik. Különböző előjelű számok összeadásakor nem keletkezhet előjeles túlcsordulás. Az utolsó bitről kilépő átvitel önmagában nem azonos az előjeles túlcsordulással. ==== 2.6. Előjelkiterjesztés ==== Ha egy előjeles számot több biten szeretnénk tárolni, a bal szélső előjelbitet kell megismételni. Például a −5 átalakítása 8 bitesről 16 bitesre: 8 biten: 11111011 16 biten: 11111111 11111011 Pozitív szám esetén balról 0-kat írunk hozzá: 8 biten: 00000101 16 biten: 00000000 00000101 Ez az //előjelkiterjesztés// megőrzi a szám eredeti értékét. ==== 3. Törtszámok kettes számrendszerben ==== A bináris törtek a tízes számrendszer törtrészeihez hasonlóan helyiértékeket használnak, de a tizedesvesszőtől jobbra 2 negatív kitevőjű hatványai állnak. ^ Bináris helyiérték ^ 2⁻¹ ^ 2⁻² ^ 2⁻³ ^ 2⁻⁴ ^ | Tízes számrendszerben | 1/2 | 1/4 | 1/8 | 1/16 | | Érték | 0,5 | 0,25 | 0,125 | 0,0625 | Például: 0,101₂ = 1 · 2⁻¹ + 0 · 2⁻² + 1 · 2⁻³ = 0,5 + 0 + 0,125 = 0,625₁₀ Egy egész- és törtrészt egyaránt tartalmazó szám: 1101,01₂ = 8 + 4 + 1 + 0,25 = 13,25₁₀ === Tízes tört átalakítása binárissá === A törtrészt ismételten megszorozzuk 2-vel. Minden lépésben az eredmény egész része adja a következő bináris számjegyet. Alakítsuk át a 0,625 értéket: ^ Művelet ^ Egész rész ^ Megmaradó törtrész ^ | 0,625 · 2 = 1,25 | 1 | 0,25 | | 0,25 · 2 = 0,5 | 0 | 0,5 | | 0,5 · 2 = 1,0 | 1 | 0 | Az egész részeket felülről lefelé összeolvasva: 0,625₁₀ = 0,101₂ Nem minden tízes számrendszerbeli tört írható fel véges bináris törtként. A 0,1 például végtelen, ismétlődő bináris tört: 0,1₁₀ = 0,00011001100110011...₂ Emiatt a számítógép a 0,1 értéknek csak egy közeli közelítését tudja eltárolni. ==== 4. Fixpontos és lebegőpontos ábrázolás ==== Törtszámok tárolására többféle megoldás használható. //Fixpontos ábrázolásnál// előre meghatározzuk, hogy a bitsorozatban hol található a bináris pont. Például egy 8 bites formátumban kijelölhetünk 4 bitet az egészrész és 4 bitet a törtrész számára. A ''00110100'' bitsorozat ekkor így értelmezhető: 0011,0100₂ = 3,25₁₀ A fixpontos számok egyszerűek és kiszámítható pontosságúak, de az ábrázolható tartományuk korlátozott. //Lebegőpontos ábrázolásnál// a bináris pont helye nem rögzített. A számot a tudományos számábrázoláshoz hasonló alakban tároljuk: előjel · mantissza · 2kitevő Például: 1101,01₂ = 1,10101₂ · 2³ A bináris pontot három hellyel mozgattuk balra, ezért a kitevő értéke 3. ==== 5. Az IEEE 754 lebegőpontos szabvány ==== A legtöbb mai számítógép az IEEE 754 szabvány szerint tárolja a lebegőpontos számokat. A bitsorozat három mezőből áll: * ''S'' – előjelbit (sign); * ''E'' – eltolt kitevő (exponent); * ''F'' – törtrészmező (fraction), amelyet gyakran mantisszának is neveznek. A két leggyakoribb formátum: ^ Formátum ^ Teljes méret ^ Előjel ^ Kitevő ^ Törtrész ^ Kitevő eltolása ^ Közelítő decimális pontosság ^ | Egyszeres pontosság (float) | 32 bit | 1 bit | 8 bit | 23 bit | 127 | kb. 7 számjegy | | Kétszeres pontosság (double) | 64 bit | 1 bit | 11 bit | 52 bit | 1023 | kb. 15–16 számjegy | Egy normál, 32 bites lebegőpontos szám értéke: (−1)^S · 1,F₂ · 2^(E − 127) ahol: * ''S'' az előjelbit; * ''E'' a kitevőmező előjel nélküli értéke; * ''F'' a törtrészmező; * 127 a kitevő eltolási értéke, más néven //bias//. Az előjelbit értelmezése: S = 0: pozitív szám S = 1: negatív szám A kitevőt eltolva tároljuk, ezért negatív kitevőhöz sincs szükség külön előjelbitre. Például a 3 kitevőt 32 bites formátumban így tároljuk: 3 + 127 = 130 = 10000010₂ Normál számok esetében a mantissza mindig ''1,'' kezdetű. A kezdő 1-est nem kell eltárolni, mert annak értéke ismert. Ezt //rejtett egyesnek// nevezzük. Így 23 tárolt törtrészbit valójában 24 bites bináris pontosságot biztosít. ==== 5.1. A 13,25 ábrázolása 32 bites IEEE 754 formátumban ==== === 1. Átalakítás kettes számrendszerbe === 13,25₁₀ = 1101,01₂ === 2. Normalizálás === 1101,01₂ = 1,10101₂ · 2³ === 3. Az előjelbit meghatározása === A szám pozitív: S = 0 === 4. A kitevő tárolása === kitevő = 3 eltolt kitevő = 3 + 127 = 130 = 10000010₂ === 5. A törtrész tárolása === A normalizált alak kezdő 1-esét nem tároljuk. Az utána következő bitek: 10101000000000000000000 === 6. A mezők összeállítása === S | E | F 0 | 10000010 | 10101000000000000000000 Egybefüggően: 01000001010101000000000000000000 Hexadecimális alakban: 0x41540000 A −13,25 ábrázolásánál csak az előjelbit változik 1-re: 11000001010101000000000000000000 = 0xC1540000 ==== 5.2. IEEE 754 bitsorozat visszaalakítása ==== Határozzuk meg a következő 32 bites lebegőpontos szám értékét: 1 | 10000001 | 01100000000000000000000 Az előjelbit: S = 1, ezért a szám negatív. A kitevőmező: 10000001₂ = 129 valódi kitevő = 129 − 127 = 2 A rejtett 1-essel kiegészített mantissza: 1,011₂ = 1 + 1/4 + 1/8 = 1,375 A szám értéke: (−1) · 1,375 · 2² = −1,375 · 4 = −5,5 ==== 5.3. Különleges értékek ==== Az IEEE 754 szabvány bizonyos kitevőmintákat különleges értékek jelölésére tart fenn. ^ Kitevőmező ^ Törtrészmező ^ Jelentés ^ | minden bit 0 | minden bit 0 | +0 vagy −0 | | minden bit 0 | nem minden bit 0 | szubnormális szám | | 1 és 254 között | tetszőleges | normál szám | | minden bit 1 | minden bit 0 | +végtelen vagy −végtelen | | minden bit 1 | nem minden bit 0 | NaN | === Pozitív és negatív nulla === Az IEEE 754 formátumban a nullának két előjeles változata van: +0: S = 0 −0: S = 1 A legtöbb műveletben egyenlőnek számítanak, de bizonyos műveleteknél az előjelüknek lehet jelentősége. === Szubnormális számok === A szubnormális számok segítségével a nullához nagyon közeli értékek is ábrázolhatók. Ezeknél nincs rejtett kezdő 1-es, ezért az értékük: (−1)^S · 0,F₂ · 2^(1 − bias) Pontosságuk kisebb, mint a normál számoké, de fokozatos átmenetet biztosítanak a legkisebb normál szám és a nulla között. === Végtelen === Pozitív vagy negatív végtelen keletkezhet például túl nagy eredmény vagy nem nulla érték nullával történő lebegőpontos osztása esetén: 1,0 / 0,0 = +végtelen −1,0 / 0,0 = −végtelen === NaN === A ''NaN'' jelentése //Not a Number//, vagyis „nem szám”. Matematikailag nem értelmezett lebegőpontos műveletek eredménye lehet, például: 0,0 / 0,0 négyzetgyök(−1,0) a valós számok között végtelen − végtelen A NaN különleges tulajdonsága, hogy még önmagával sem tekinthető egyenlőnek: if (x != x) { /* x értéke NaN */ } A gyakorlatban azonban erre a célra a programozási nyelvek beépített ''isnan'' függvényét célszerű használni. ==== 6. A lebegőpontos ábrázolás pontatlansága ==== A lebegőpontos számok véges számú bitből állnak, ezért a valós számoknak csak egy véges részhalmazát tudják pontosan tárolni. A többi értéket a legközelebbi ábrázolható számra kell kerekíteni. Mivel a 0,1 és a 0,2 nem írható fel véges bináris törtként, a következő összehasonlítás egyes programozási nyelvekben meglepő eredményt adhat: double a = 0.1; double b = 0.2; if (a + b == 0.3) { printf("Egyenlo"); } else { printf("Nem egyenlo"); } Az eredmény rendszerint ''Nem egyenlo'', mert a tárolt közelítő értékek összege nem pontosan egyezik meg a 0,3 tárolt közelítésével. Lebegőpontos értékeket ezért általában egy kis hibahatár, úgynevezett //epsilon// segítségével hasonlítunk össze: #include double epsilon = 1e-9; if (fabs((a + b) - 0.3) < epsilon) { printf("A ket ertek a megadott pontossagon belul egyenlo"); } Az epsilon megfelelő értéke a számok nagyságrendjétől és az alkalmazás pontossági követelményeitől függ; nem minden feladathoz jó ugyanaz az állandó. ==== 6.1. Pontosság és nagyságrend ==== A lebegőpontos számábrázolás relatív pontosságú. Minél nagyobb számokat tárolunk, annál nagyobb távolság lehet két egymást követő ábrázolható érték között. A 32 bites ''float'' 24 bites bináris pontossága miatt minden egész számot pontosan képes ábrázolni ''2²⁴ = 16 777 216'' értékig. E fölött már nem minden egymást követő egész szám tárolható. Például 32 bites float esetén: 16 777 216 még pontosan ábrázolható. 16 777 217 már nem ábrázolható pontosan. Ezért lebegőpontos típust nem célszerű például nagy azonosítók vagy pénzösszegek pontos tárolására használni. ==== 6.2. Túlcsordulás és alulcsordulás ==== //Túlcsordulás// akkor történik, ha az eredmény abszolút értéke nagyobb a formátumban tárolható legnagyobb véges számnál. Az eredmény ilyenkor gyakran pozitív vagy negatív végtelen lesz. //Alulcsordulás// akkor történik, ha egy nem nulla eredmény abszolút értéke túl közel kerül a nullához. Az eredmény először szubnormális számmá, végül nullává kerekedhet. ==== 6.3. Kerekítési hibák felhalmozódása ==== Egyetlen lebegőpontos művelet hibája rendszerint nagyon kicsi, de sok egymás utáni művelet során a hibák felhalmozódhatnak. A műveletek sorrendje is befolyásolhatja az eredményt. Például nagyon nagy és nagyon kicsi szám összeadásakor a kis szám hatása elveszhet: nagy_szam + kis_szam = nagy_szam Ez nem matematikai azonosság, hanem a véges pontosság következménye. ==== 7. Egész és lebegőpontos számábrázolás összehasonlítása ==== ^ Tulajdonság ^ Kettes komplemens egész szám ^ IEEE 754 lebegőpontos szám ^ | Tárolható értékek | egészek | törtrészes és nagyon kis vagy nagy számok | | Pontosság | a tartományon belül pontos | gyakran közelítő | | Előjel kezelése | a legmagasabb bit helyiértékének része | külön előjelbit | | Túlcsordulás | a bitsorozat hibás előjelű értékké fordulhat vagy a nyelv hibát jelezhet | gyakran végtelen keletkezik | | Különleges értékek | általában nincsenek | ±0, ±végtelen, NaN, szubnormális számok | | Jellemző alkalmazás | számlálók, indexek, darabszámok | mérések, geometriai és tudományos számítások | ==== 8. Összefoglalás ==== * Az előjel nélküli ''n'' bites egész számok tartománya ''0 ... 2ⁿ − 1''. * Az előjeles egész számokat a modern számítógépek jellemzően kettes komplemens alakban tárolják. * Egy negatív kettes komplemens számot a pozitív alak bitjeinek megfordításával, majd 1 hozzáadásával kapunk meg. * Az ''n'' bites kettes komplemens számok tartománya ''−2ⁿ⁻¹ ... 2ⁿ⁻¹ − 1''. * A bináris törtrész helyiértékei ''2⁻¹, 2⁻², 2⁻³, ...'' értékek. * Az IEEE 754 lebegőpontos szám előjelből, eltolt kitevőből és törtrészből áll. * Sok tízes tört, például a 0,1, binárisan csak közelítőleg tárolható. * Lebegőpontos számokat általában nem közvetlen egyenlőségvizsgálattal, hanem megfelelő hibahatár figyelembevételével hasonlítunk össze. * Pénzügyi és más, pontos tizedes számítást igénylő feladatokhoz célszerű decimális vagy skálázott egész számábrázolást használni. ==== 9. Ellenőrző kérdések és feladatok ==== - Miért nem határozható meg egy bitsorozat értéke az adattípus ismerete nélkül? - Mi a 45 előjel nélküli 8 bites bináris alakja? - Ábrázolja a −18 számot 8 bites kettes komplemens formában! - Milyen értéket jelent a ''11101100'' bitsorozat 8 bites kettes komplemens számként? - Mi a 8 bites előjeles egészek legkisebb és legnagyobb értéke? - Végezze el 8 biten a ''12 + (−7)'' összeadást! - Miért okoz túlcsordulást 8 biten a ''100 + 40'' összeadás? - Alakítsa át a ''101,101₂'' számot tízes számrendszerbe! - Alakítsa át a ''10,625₁₀'' számot kettes számrendszerbe! - Milyen három fő mezőből áll egy IEEE 754 lebegőpontos szám? - Miért nem tárolható pontosan a 0,1 a szokásos bináris lebegőpontos formátumokban? - Mit jelent a NaN érték? ==== 10. A feladatok rövid megoldása ==== - A bitsorozat jelentése az ábrázolási módtól és az adattípustól függ. - ''45₁₀ = 00101101₂''. - ''+18 = 00010010'', bitfordítás után ''11101101'', majd 1 hozzáadásával ''−18 = 11101110''. - ''11101100₂ = −20₁₀''. - A tartomány −128-tól 127-ig tart. - ''00001100 + 11111001 = 00000101'', tehát az eredmény 5. - Az eredmény 140 lenne, amely nagyobb a legnagyobb 8 bites előjeles értéknél, vagyis 127-nél. - ''101,101₂ = 5,625₁₀''. - ''10,625₁₀ = 1010,101₂''. - Előjelbitből, kitevőmezőből és törtrészmezőből. - Mert a 0,1 bináris alakja végtelen, ismétlődő tört, miközben a tárolásra véges számú bit áll rendelkezésre. - A NaN matematikailag nem értelmezhető lebegőpontos művelet eredményét jelöli.