# 11. hét: számrendszerek, számábrázolás

Czirkos Zoltán · 2025.11.14.

A számrendszerekről szóló előadáshoz kapcsolódó feladatok. Néhány számábrázolási probléma megjelenése a programokban.

Felkészülés a laborra:

- Az operátorokról tanultak átismétlése.
- A számábrázolásról, számrendszerekről tanultak megértése.

## 1. Operátorok és kiértékelés

Adjuk meg az alábbi kifejezésekhez tartozó kifejezésfát, figyelembe véve az operátorok precedenciáját!

- `6 + 2 * 3`
- `2 * 6 - 5 / 3`
- `a = b + c`
- `t[i + 2] * 3`
- `5 * - 6`
- `5 - * 6`

A legegyszerűbb ascii karakterekkel megrajzolni és úgy feltölteni. Ebben a feladatban nem kell kódolni, csak rajzolni.

    Kétszer kettő:

      *
     / \
    2   2

Megoldás

## 2. Lebegőpontos

Mit írnak ki az alábbi program egyes sorai? Próbáld meg kitalálni! Futtasd le a programot, és magyarázd meg az eredményt!

    print("1.", 1e200 / 1e-200)
    print("2.", "igaz" if 1e3 + 1 == 1e3 else "hamis")
    print("3.", "igaz" if 1e30 + 1 == 1e30 else "hamis")

Megoldás

Az 1-eshez: 10200/10-200 = 10400 lenne. De ez már nem fér bele a `float` típus ábrázolási tartományába, ezért végtelent kapunk.

Az 2-es és 3-as sorhoz: míg a 103+1 értéke különbözik 103-től, mivel a két szám, 1000 és 1 nem tér el túlzottan egymástól nagyságrendben. A 1030 és 1 esetén ez már nem igaz.

## 3. Hol a hiba?

Az alábbi program egy színátmenetes rajzot hivatott elkészíteni. A színek balról jobbra egyre pirosabbak, és alulról felfelé egyre kékebbek. A színkomponensek értéke mindig 0-tól 1-ig változik. Az osztások számát a program a felhasználótól kéri, az egyes kis négyzetek oldalhossza 30 képpont.

A program némely osztásszámra kifogástalanul működik, például `db = 5`-re a képen látható ábrát készíti. Más osztások esetén elromlik, `db = 10` esetén szétcsúszik az ábra.

Mi a probléma? Magyarázd meg a jelenséget, és mutasd meg a nyomkövetőben is! Javítsd meg a programot, hogy minden osztásszámra helyesen működjön!

    import turtle

    def negyzet(a):
        turtle.begin_fill()
        for i in range(0, 4):
            turtle.forward(a)
            turtle.left(90)
        turtle.end_fill()

    def main():
        a = 30  # oldalhossz
        db = int(input("Hány darabból? "))

        turtle.speed(0)
        b = 0.0
        while b <= 1.0:
            r = 0.0
            while r <= 1.0:
                turtle.fillcolor(r, 0, b)
                negyzet(a)
                turtle.forward(a)
                r += 1/(db-1)
            turtle.backward(db*a)
            turtle.left(90)
            turtle.forward(a)
            turtle.right(90)
            b += 1/(db-1)

        turtle.done()

    main()

Megoldás

A lebegőpontos számítás pontatlansága. Az alábbi programocska jól mutatja a problémát `db = 10` esetén:

    db = 10

    egesz = 0
    valos = 0.0
    while egesz <= db:
        print(egesz, valos)
        egesz += 1
        valos += 1 / (db-1)

Nem szabad `float` változóra építeni a ciklust, ha tudni akarjuk, hogy pontosan hányszor fut a törzse. Márpedig itt igen, mert pontosan 10 darab négyzetet kell rajzolni, nem 11-et vagy 9-et valamiféle kerekítési/számítási hiba miatt.

    def javitott():
        a = 30  # oldalhossz
        db = int(input("Hány darabból? "))

        turtle.speed(0)
        for b in range(db):
            for r in range(db):
                turtle.fillcolor(r/(db-1), 0, b/(db-1))
                turtle.begin_fill()
                negyzet(a)
                turtle.forward(a)
            turtle.backward(db*a)
            turtle.left(90)
            turtle.forward(a)
            turtle.right(90)

        turtle.done()

## 4. Kiírás adott számrendszerben I.

Írj programot, amelyik a megadott számot a megadott számrendszerbe alakítja át, és beteszi úgy egy sztringbe! Pl. a 9-es szám 2-es számrendszerben `"1001"`. Az algoritmusod tetszőleges lehet, de a végén egy `print(szam)` utasítással ki kell tudni írni a számot, ahol a `szam` annak a változónak a neve, amiben az eredményt előállítottad.

Elég, ha előbb csak tízes számrendszerig működik a program! Ha működik jól a programod, akkor utána alakítsd át úgy, hogy nagyobb alap esetén is működjön! A 10-et, és annál nagyobb számjegyeket ilyenkor betűkkel szokás jelölni. Pl. 16-osban a 0…15 számjegyek: 012…89ABCDEF.

Tipp

Az alapötlet az, hogy maradékképzéssel látod a szám legutolsó számjegyét, utána pedig egész osztással le tudod vágni azt az utolsó számjegyet, amit kezeltél.

Szám | %10 | //10
--- | --- | ---
1234 | 4 | 123
123 | 3 | 12
12 | 2 | 1
1 | 1 | 0

Megoldás

Az alsó számjegy meghatározása által mindig a legutolsó számjegyet kapjuk, vagyis fordított sorrendben állnak elő az adatok. A sorrend megfordítását sokféleképpen megoldhatjuk: például mindig a sztring elé fűzünk, esetleg mindig a végére, és a végeredményt fordítjuk meg, vagy használhatunk akár rekurziót is.

    def atalakit(szam, szamrendszer):
        # iteratív változat, csak pozitív számokra és 2-16 közötti számrendszerekre, nullára nem működik...
        szamjegyek = "0123456789ABCDEF"
        szamstr=""
        while szam > 0:
            maradek = szam % szamrendszer
            szamstr =  szamjegyek[maradek]+ szamstr
            szam = szam // szamrendszer
        return szamstr

    # rekurzív változat, csak pozitív számokra és 2-16 közötti számrendszerekre, nullára nem működik...
    def atalakit_rekurziv(szam, szamrendszer):
        szamjegyek = "0123456789ABCDEF"
        if szam == 0:
            return ""
        else:
            return atalakit_rekurziv(szam // szamrendszer, szamrendszer) + szamjegyek[szam % szamrendszer]

## 5. Kiírás adott számrendszerben II.

Működik az előző feladatban megírt programod 0-ra is? Az algoritmustól függően előfordulhat, hogy 0-ra üres sztringet ad. Ha ilyen megoldást adtál, akkor egészítsd ki!

Oldd meg továbbá azt, hogy negatív számot is kaphasson! A pozitív számokat nem kell plusszal jelölni, a negatívak elé viszont kerüljön mínusz karakter!

Megoldás

    # valójában hátultesztelő ciklikus kellene, de ezt csak szimulálni tudjuk Pythonban
    def atalakit(szam, szamrendszer):
        szamstr=""
        if szam < 0:
            szam = -szam
            elojel = "-"
        else:
            elojel = ""
        while True:
            szamjegy =  szam % szamrendszer
            karakter= chr( ord('0') + szamjegy if szamjegy < 10 else ord('A') + (szamjegy - 10))
            szamstr =  karakter + szamstr
            szam = szam // szamrendszer
            if szam == 0:
                break
        return elojel + szamstr

## 6. Beolvasás adott számrendszerben

Írj programot, amely kap egy sztringet és egy számrendszernek a számát, majd megadja azt az egész számot, amit az adott sztringbeli számjegysorozat reprezentál! Például ha a felhasználó beírja, hogy 2-es számrendszer, és 1101, akkor ki kell írni, hogy 13. Először elég, ha tízes számrendszerig működik a program, csak utána írd át úgy, hogy működjön nagyobb alap esetén is!

    Hányas számrendszerben fogsz írni?
    16
    Ird be a szamot!
    fce2
    A beolvasott szám 10-es számrendszerben: 64738

Ha elkészültél a saját algoritmussal, ellenőrizd különféle számokra a beépített konverzióval! Pl. `int("fce2", 16)` a fenti példa alapján `64738`-at ad.

Tipp

Ehhez végig kell haladni a sztringen, és a meglévő adathoz (megszorozva persze az alappal) mindig hozzáadható az új érték:

Eddig megvolt | Új számjegy | Új érték
--- | --- | ---
0 | 1 | 1
1 | 2 | 12
12 | 3 | 123
123 | 4 | 1234

Megoldás

### Megoldási terv, ötletek

- Beolvasunk egy számjegyet (karaktert), és kivonjuk belőle a `'0'` karakterkódját. Így megkapjuk az értékét.
- Hogy lesz egy számjegyből sok? Pl. 123 esetén, ha a 12-t már beolvastuk, és jön még egy 3-as, akkor a 12 igazából a 120-at jelentette (megszorozzuk 10-szel), és ahhoz adjuk a 3-at. Ha 123 esetén 4-es jön, akkor igazából 1230 volt, és ahhoz adjuk a 4-et.
- Vagyis mindig az eddigi, szorozva 10-zel (a számrendszer alapjával), plusz az új számjegy.

Az egyszerű számításhoz felhasználhatjuk a Horner-elrendezést. Vegyük példának ehhez 10-es számrendszerben a 234-et! Ezt az egyes számjegyekből `2×102+3×101+4×100` formában határozhatjuk meg. Ami pedig ugyanaz, mint `((2×10)+3)×10+4`, azaz `(((0×10+2)×10)+3)×10+4`, amiből már látszik, hogyan kell dolgoznunk: mindig a meglévő részeredményt megszoroznunk tízzel, és hozzáadni az új számjegy értékét. Ha eddig a 23-at láttuk, és megkapjuk a 4-est, akkor a szóban forgó lépés `23×10+4 = 230+4` lesz. Ezt kell folytatni egészen addig, amíg határoló karaktert nem kapunk, persze a tízzel szorzás helyett az adott számrendszer alapját tekintve.

A számjegy beolvasásánál figyelembe kell venni, hogy bár számjegyről beszélünk, tízes számrendszer fölött ez lehet betű is. Ezért a beolvasott karaktert meg kell vizsgálni, számjegyről van-e szó (0...9) vagy betűről (a...z). Ha számjegyről, akkor a `'0'`-s számjegy karakterkódját kell kivonni belőle, hogy számértéket kapjunk, amúgy pedig az `'a'` betű karakterkódját, és hozzáadni 10-et, mert `A=10`, `B=11` stb. Itt kapóra jönnek a sztring típus függvényei: `.isdigit()` = számjegy-e?, `.isalpha()` = betű-e?, `.upper()` = a karakter nagybetűként (ha szépen szeretnénk csinálni).

    def beolvas(szoveg, szamrendszer):
        szam = 0
        for karakter in szoveg:
            if karakter.isdigit():
                ertek = ord(karakter) - ord('0')
            else:
                ertek = ord(karakter.upper()) - ord('A') + 10
            szam = szam * szamrendszer + ertek
        return szam

## 7. Konverziós függvények

Dolgozd át úgy az előző feladatok „kiírás adott számrendszerben” és „beolvasás adott számrendszerben” programkódjait, hogy azok függvények legyenek! Például:

- `szamrendszerbol("fce2", 16)` → `64738`.
- `szamrendszerbe(64738, 16)` → `"FCE2"`.

Készítsd fel a függvényeket hibák kezelésére! Pl. ha adott számrendszerben nem létezik egy számjegy, akkor dobjon kivételt az első függvény. Ha negatív, vagy nullás/egyes számrendszerbe átalakítást kérünk, akkor dobjon kivételt a második, és így tovább. Milyen hibalehetőségek lehetnek még?

Megoldás

    # végleges megoldások hibakezeléssel 26 karakterig megyünk, max 36-os számrendszer engedélyezett
    def szamrendszerbol(szoveg, szamrendszer):
        # átadott paraméterek ellenőrzése
        if len(szoveg) == 0:
            raise ValueError("A szöveg nem lehet üres!")
        if szamrendszer < 2 or szamrendszer > 36:
            raise ValueError("A számrendszer 2 és 36 között kell, hogy legyen!")
        negativ=False
        if szoveg[0] == '-':
            negativ = True
            szoveg = szoveg[1:]
        elif szoveg[0] == '+':
            szoveg = szoveg[1:]
        if len(szoveg) == 0:
            raise ValueError("Csak előjelet tartalmay!")
        szam = 0
        for karakter in szoveg:
            if karakter.isdigit():
                ertek = ord(karakter) - ord('0')
            elif karakter.upper().isalpha():
                 ertek = ord(karakter.upper()) - ord('A') + 10
            else:
                raise ValueError(f"Érvénytelen karakter: {karakter}")
            if ertek >= szamrendszer:
                raise ValueError(f"A(z) {karakter} nem érvényes a {szamrendszer}-es számrendszerben!")
            szam = szam * szamrendszer + ertek
        if negativ:
            szam = -szam
        return szam

    def szamrendszerbe(szam, szamrendszer):
        # átadott paraméterek ellenőrzése
        if szamrendszer < 2 or szamrendszer > 36:
            raise ValueError("A számrendszer 2 és 36 között kell, hogy legyen!")
        szamstr=""
        if szam < 0:
            szam = -szam
            elojel = "-"
        else:
            elojel = ""
        while True:
            szamjegy= szam % szamrendszer
            szamstr =  ((chr( ord('0')+szamjegy)) if szamjegy < 10 else chr( ord('A')+szamjegy-10)) + szamstr
            szam = szam // szamrendszer
            if szam == 0:
                break
        return elojel + szamstr