Tudjuk, hogy a float típusú tizedes törtek esetében a véges számú biten történő ábrázolás miatt általában nem az egzakt értéket, hanem annak valamilyen pontosságú közelítését kapjuk. Ez a pontosság attól függ, hogy az adott platform a lebegőpontos számokat hány biten tárolja. Egy tizedes tört ténylegesen tárolt értékét megkaphatjuk a float típusú számra meghívott hex() metódussal. Ez egy karakterláncot ad vissza, amely sajátos módon írja le a szám értékét. Ugyanis ebben mind hexadecimálisan, mind decimálisan értelmezendő számok találhatók.
Vegyük például az x = 0.456 tizedes törtet. Az x.hex() hívás eredménye a 0x1.d2f1a9fbe76c9p-2 karaktersor lesz. Ez lényegében egy hatványkitevős alakot ír le, ahol a p betű a bináris alapot, azaz a 2-t jelenti. A p utáni szám a kitevő decimális formában. A 0x a hexadecimális formára utal, és ennek megfelelően a 0x és a p betű közötti rész egy hexadecimális formában ábrázolt törtet jelent, vagyis a mantissza hexadecimális számként van kifejezve.
Ha most arra vagyunk kíváncsiak, hogy az így leírt szám milyen bináris törtnek felel meg, akkor ennek megállapítása a hex() metódus eredményeként kapott karakterlánc alapján nem egy nyilvánvaló, magától értetődő feladat. Ezért e karaktersorozat feldolgozásához és a neki megfelelő bináris tört meghatározásához egy floathex_to_bin nevű függvényt készítünk, amelynek egy lehetséges megvalósítása alább látható. Az egyszerűség kedvéért a konvertálás most csak az egynél kisebb pozitív float számokra végezhető el.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
from decimal import Decimal, getcontext def floathex_to_bin(floathex: str) -> str: """Az argumentum egy olyan karakterlánc, amely egy pozitív, egynél kisebb float típusú számra meghívott hex() metódus visszatérési értéke. A függvény az ezen karaktersorozattal leírt szám bináris tört értékét adja vissza nulla egész résszel és törthatárolójellel egy karakterláncban. Pl. '0.0101001'. """ # Hexadecimális számjegyekhez azok decimális értékét hozzárendelő szótár. hexval = {h: decval for h, decval in zip('0123456789abcdefABCDEF', list(range(10)) + [*range(10, 16)] * 2)} # A mantissza és kitevő szétválasztása. mant, exp = floathex.split('p') mant = mant[2:] # a '0x' leválasztása, hogy csak tisztán a hexadecimális törtet kapjuk meg. # A hexadecimális tört egész és tört részének különválasztása. # Ez majd a végén a törtrész határolójel megfelelő pozicionálásához kell. hexint, hexfrac = mant.split('.') # A mantissza hexadecimális jegyeinek bináris formába írása. mantissa_bin = [bin(hexval[h])[2:].zfill(4) for h in mant if h != '.'] # A törthatárolójel nélküli bináris jegyű karaktersorozat elállítása. mantissa_bin_str = ''.join(mantissa_bin) # A törthatárolójel beszúrási indexének meghatározása. delimiter_index = len(hexint) * 4 + int(exp) # A konverzió eredményét jelentő bináris törtnek megfelelő karaktersorozat mint visszatérési érték # előállítása, ami a törthatárolójel beszúrását és a felesleges vezető nullák eltávolítását foglalja magában. return '0' + '.'.join((mantissa_bin_str[:delimiter_index], mantissa_bin_str[delimiter_index:])).lstrip('0') def bin_to_dec_fraction(bin_frac: str) -> str: """Az argumentum egy bináris törtet jelentő karakterlánc. A visszatérési érték az ennek megfelelő tizedes tört karakterlánc formában. """ if '.' in bin_frac: bin_frac = bin_frac[2:] return str(sum([Decimal(c) * Decimal(2) ** -n for n, c in enumerate(bin_frac, 1)])) |
Annak megállapítására, hogy egy bináris törtet jelentő karaktersorozat milyen tizedestörtnek felel meg egy újabb függvényt definiálunk bin_to_dec_fraction néven, amely szintén az előbbi kódban látható. Ebben a Decimal típust használtunk, hogy a számításoknál a float esetleges ábrázolási hibáit kiküszöböljük.
Az előbbi függvényeket a következő tesztsorokban használtuk fel néhány float típusú tört bináris tört formájának meghatározására és azok helyességének ellenőrzésére. A decimális értékek egyezősége alapján láthatjuk, hogy a tizedes törtek ténylegesen ábrázolt értékének megfelelő bináris törtek helyesen állnak elő.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# TESZT getcontext().prec = 100 for x in (0.1, 0.3, 0.456): fh = x.hex() print('\n({}).hex() = {} -> bináris tört: {}'.format(x, fh, floathex_to_bin(fh))) df = bin_to_dec_fraction(floathex_to_bin(fh)) print('A bináris törtből számolt tizedes tört tényleges értéke: \n{}'.format(df.rstrip('0'))) # A float típusú tört összes értékes jegyének megjelenítése (számvégi nullák elhagyása). float_full = '{:.100f}'.format(x).rstrip("0") print('Ellenőrzés: a {} összes értékes jeggyel kiírva:\n{}'.format(x, float_full)) # Eredmény # (0.1).hex() = 0x1.999999999999ap-4 -> bináris tört: 0.00011001100110011001100110011001100110011001100110011010 # A bináris törtből számolt tizedes tört tényleges értéke: # 0.1000000000000000055511151231257827021181583404541015625 # Ellenőrzés: a 0.1 összes értékes jeggyel kiírva: # 0.1000000000000000055511151231257827021181583404541015625 # # (0.3).hex() = 0x1.3333333333333p-2 -> bináris tört: 0.010011001100110011001100110011001100110011001100110011 # A bináris törtből számolt tizedes tört tényleges értéke: # 0.299999999999999988897769753748434595763683319091796875 # Ellenőrzés: a 0.3 összes értékes jeggyel kiírva: # 0.299999999999999988897769753748434595763683319091796875 # # (0.456).hex() = 0x1.d2f1a9fbe76c9p-2 -> bináris tört: 0.011101001011110001101010011111101111100111011011001001 # A bináris törtből számolt tizedes tört tényleges értéke: # 0.456000000000000016431300764452316798269748687744140625 # Ellenőrzés: a 0.456 összes értékes jeggyel kiírva: # 0.456000000000000016431300764452316798269748687744140625 |
A float típus számábrázolási pontosságáról és vonatkozásairól a Python tudásépítés lépésről lépésre című e-könyvben a „Műveletek” fejezet „Számítási pontosság float típus esetén” című szakaszában, valamint a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezet „Matematikai számítások támogatása” alfejezetben van kiemelten szó. A float típusra értelmezett hex() metódus és annak inverze a fromhex() metódus leírása példákkal a „Beépített típusok nyilvános metódusai” fejezetben található.