Seite anzeigenÄltere VersionenLinks hierherCopy this pageFold/unfold allNach oben Diese Seite ist nicht editierbar. Du kannst den Quelltext sehen, jedoch nicht verändern. Kontaktiere den Administrator, wenn du glaubst, dass hier ein Fehler vorliegt. ====== - Zahlensysteme & Datentypen ====== ++++Lernziele| Grundsätzlich kann alles bis und mit Kapitel "Weitere Aufgaben" geprüft werden. Die folgenden Lernziele dienen als eine Art Checklist, sind aber nicht zwingend komplett: * Wissen, warum Datentypen wichtig sind in C Sprachen. * Dynamische und statische Typisierung vergleichen. Vor- und Nachteile? * Zahlen in wissenschaftlicher Schreibweise darstellen. * Umwandlung von ganzen Zahlen (ints) dezimal <-> binär * Binärzahl in Gleitkommadarstellung bringen ... * ... und zugehörige Fachbegriffe (Mantisse, ...) erklären können. * Umwandlung von Gleitkommazahlen dezimal <-> binär * Umwandlung Gleitkommadarstellung in Binärzahl oder Dezimalzahl * Sonderfälle für binäre Gleitkommazahlen kennen und verstehen. * Binäre floats addieren können. * Codes zu den obigen Punkten verfassen können in Python oder C#. ++++ ++++Praktische Python Befehle| <code python> # Length len("laenge von einem String oder Liste ...") # Data types str(42) int("132") float(13) type(42) bin(132) # Strings s = "ich bin ein String" s[2:] s[4:7] s[:5] s.replace('b','B') li = s.split(' ') s.strip(" hallo ") s.lstrip(" hallo ") s.rstrip(" hallo ") s = s.upper() s = s.lower() s.find("bin") many_zeros_string = "0" * 100 ## TIPP: In diesem Thema bietet es sich meist an, mit Strings anstelle Listen zu arbeiten. Sollte man aber das Bedürfnis nach Listen haben, hier die wichtigsten Befehle: li = ['k','s','r'] len(li) li.append('romanshorn') li.pop(2) li.remove('k') print(li) </code> ++++ ===== - Voraussetzungen ===== Aus dem Grundlagenfach weisst du bereits, was Zahlensysteme sind. Insbesondere kennst du das Binärsystem und kannst: * Umrechnen: Dezimalsystem <-> Binärsystem * Darstellung negativer Zahlen * Rechnen mit Binärzahlen: Addition, Multiplikation, Subtraktion * mithilfe des Binärsystems Mitmenschen (die es verdient haben) eloquent beleidigen können * Gegeben $x-$Bits für Darstellung -> min./max. Zahl bestimmen, die damit dargestellt werden kann Das Dossier **Zahlensysteme** aus dem Grundlagenfach findest du hier: {{ :ef_informatik:gfif_zahlensysteme_dossier.pdf |Dossier Zahlensysteme (GFIF)}} ===== - Datentypen ===== Beim Programmieren kennen wir viele verschiedene **Datentypen** wie: * **Integer** (kurz: int): also ganze Zahlen $\ldots,-3,-2,-1,0,1,2,3,\ldots$ * **Floats:** Gleitkommazahlen, z.B. $3.14$ * **Strings:** Text, z.B. `"Das EFIF ist super!"` * und viele weitere Für einen Computer macht es einen grossen Unterschied, ob es sich bei einer Variablen um einen Integer oder einen Float handelt, da diese im Hintergrund **ganz anders gespeichert** werden. Zum Beispiel ist für ein Computer `4` (int) nicht das Gleiche wie `4.0` (float), auch wenn die beiden Zahlen mathematisch identisch sind. ==== - Datentypen in Python ==== In **Python** muss man sich nicht wirklich um Datentypen kümmern, was sowohl eine der grössten Stärken wie auch Schwächen von Python ist. Zum Beispiel kann man in Python eine Variable ganz einfach direkt festlegen: <code python> i = 42 f = 3.14 s = "Das EFIF ist super!" </code> Python erkennt dann automatisch, um was für einen **Datentypen** es sich handelt. Überzeuge dich selbst mit `print(type(...))`, dass es sich hier um die folgenden Datentypen handelt: <code python> print(type(i)) # Output: <type 'int'> print(type(f)) # Output: <type 'float'> print(type(s)) # Output: <type 'str'> </code> In Python kann man eine Variable auch problemlos in einen anderen Typen umwandeln: <code python> i = 42 # zuerst int i = "Ich bin jetzt ein String, holt mich hier raus!" # jetzt ploetzlich String </code> Aus diesem Grund spricht man in Python von **dynamischer Typisierung**. ==== - Statische Typisierung ==== In vielen anderen Programmiersprachen wie C\#, C\+\+ oder Java geniesst man diese Freiheiten nicht. Man spricht dann von **statischer Typisierung** und **typsichere Sprachen**. In solchen müssen Variablen **explizit deklariert**, d.h. es muss angegeben werden, für welchen Datentypen man diese Variable verwenden möchte. Einmal festgelegt, kann der Datentyp nicht mehr verändert werden. Möchte man zum Beispiel einem Integer einen Float zuweisen, so erhält man eine Fehlermeldung: Zum Beispiel in C\+\+ (was wir für Arduino brauchen werden): <code c++> int i; // separate Deklaration ... i = 42; // und Wertzuweisung float f = 3.14; // Deklaration und Wertzuweisung in einer Zeile </code> Von nun an wollen wir uns nur noch mit *typsicheren Programmiersprachen* beschäftigen. ===== - Ganze Zahlen ===== Deklariert man eine Integer-Variable `int x;`, so wird im Arbeitsspeicher für die Variable x der Speicherplatz reserviert, der einem Integer zusteht. Weise ich der Variable nun einen zu grossen Wert zu, so kann dieser nicht gespeichert werden. In dem Fall muss man auf einen Datentypen ausweichen, dem mehr Speicherplatz zur Verfügung steht. <box 100%> **Aufgabe:** Für einen Integer sollen magere $4$ Bits zur Verfügung gestellt werden. Was ist die grösste und kleinste Dezimalzahl, die man damit Darstellen kann? ++++Lösung| Insgesamt können $2^4 = 16$ verschiedene Zahlen damit dargestellt werden. Der Datentyp `int` beinhaltet sowohl positive als auch negative Zahlen: * kleinste Zahl: $-8$ * grösste Zahl: $7$ {{ :ef_informatik:2erkomplement.png?400 |}} Möchte man nur Zahlen $\geq 0$ haben, so spricht man von einem **unsigned int (uint)**. Bei $4$ Bits ist die grösste Zahl dann also $15$. ++++ </box> <box 100%> **Aufgabe:** Für einen Integer sollen nun $n$ Bits zur Verfügung stehen. Finde eine Formel für: * Anzahl verschiedene Zahlen * die kleinste und grösste Zahl <nodisp 1> ++++Lösung| * Anzahl: $2^n$ * int: * kleinste Zahl: $-2^{n-1}$ * grösste Zahl: $2^{n-1}-1$ * uint: * kleinste Zahl: $0$ * grösste Zahl: $2^{n}-1$ ++++ </nodisp> </box> Wir werden bald mit **Arduinos**, genauer Arduino Unos, arbeiten. Für diese stehen uns drei **Datentypen** für das speichern von ganzen Zahlen zur Verfügung: * **int:** 16 Bit (negative und positive Zahlen) * **unsigned int:** 16 Bit (nur positive Zahlen: $0,1,2,\ldots$) * **long:** 32 Bit <box 100%> **Aufgabe:** Du möchtest mit einem Arduino eine Stoppuhr bauen und in einer Variablen `int t;` die Zeit in Millisekunden speichern. 1. Für welche Zeitdauer geht dies gut? 1. Möchtest du längere Zeiten messen, musst du deinen Code anpassen. Was sind deine Optionen? Reichen diese Änderungen? <nodisp 1> ++++Lösung| 1. Nur für knapp 33 Sekunden! 1. Datentyp ändern in: 1. unsigned int: Reicht für $2^{16}-1\,\text{ms}$, also knapp $66$ Sekunden. Achtung: $-1$ weil kleinste Zahl $0$ ist für unsigned int. 1. long: Reicht für knapp $25$ Tage. D.h. dies kann ein Problem werden für Projekte, die länger laufen sollen (z.B. eine Uhr) ++++ </nodisp> </box> ===== - Gleitkommazahlen ===== Hilfreiches Online-Tool: [[https://zahlensysteme-rechner.de]] Das Rechnen mit ganzen Zahlen am Computer ist relativ problemfrei. So kann man davon ausgehen, dass der Computer einem immer *exakte* Resultate liefert - zumindest so lange man nicht den Bereich verlässt, der vom Datentyp (Z.B. int oder long) abgedeckt wird. Zum Beispiel kann man mit einem 32-Bit-Datentyp für ganze Zahlen (z.B. long auf Arduino Uno) //sämtliche ganzen Zahlen beschreiben//, die es im Bereich $-2147483648$ bis $2147483647$ gibt. Bei Gleitkommazahlen, also Zahlen mit Nachkommastellen, sieht es da anders aus. Beispielsweise gibt es nur schon im Intervall zwischen $0$ und $1$ *unendlich viele rationale und reelle Zahlen!* Dies führt dazu, dass Werte meist gerundet werden müssen und damit *nicht exakt dargestellt* werden können. Rechnet man dann mit diesen gerundeten Werten weiter, so können sich diese Ungenauigkeiten verstärken. Daraus resultiert folgende <color red>**Programmierweisheit:** Wenn immer möglich, sollte man mit Integers (int/long/uint/...) erledigen. Gleitkommazahlen (float/double) sollte man nur verwenden, wenn es nicht anders geht!</color> ==== Wissenschaftliche Schreibweise von Dezimalzahlen ==== Um zu verstehen, wie Gleitkommazahlen in Computern gespeichert werden, lohnt es sich, sich die **wissenschaftliche Schreibweise**, in Erinnerung zu rufen. Diese Schreibweise solltest du aus dem Mathematikunterricht kennen. In dieser Schreibweise hat eine Zahl immer die folgende Form: $$\pm a \times 10^b$$ * Die Stelle ganz links beinhaltet das **Vorzeichen** und entscheidet deshalb darüber, ob die Zahl positiv oder negativ ist. * Die Zahl $a$ vor der Potenz wird **Mantisse** genannt und erfüllt die Bedingung $1\leq a < 10$. Sie ist also eine Gleitkommazahl, wobei *genau eine* Ziffer ausser $0$ vor dem Dezimalpunkt steht. * Die Zahl $b$ wird **Exponent** genannt. Grosse Zahlen haben einen positiven, kleine einen negativen Exponenten. Du kannst dir vorstellen, dass der Exponent das Komma *verschiebt*. Zum Beispiel sieht die wissenschaftliche Schweibweise der Zahl $-72024$ wie folgt aus: $$-7.2024 \cdot 10^4$$ Gut an der wissenschaftlichen Schreibweise ist, dass sie eine Zahl *eindeutig* beschreibt. Besonders nützlich ist sie für sehr kleine und grosse Zahlen. <box 100%> **Aufgabe:** Bringe in die wissenschaftliche Schreibweise:\\ \\ 1. $100023=$\\ \\ 1. $0.000000000932=$\\ \\ 1. $613453 \cdot 10^{84}=$ Fun fact: Die letzte Zahl entspricht in ihrer Grössenordnung ungefähr der geschätzten Anzahl Atome im sichtbaren Teil des Universums!\\ \\ <nodisp 1> ++++Lösung| 1. $100023 = 1.00023 \cdot 10^5$\\ \\ 1. $0.000000000932 = 9.32 \cdot 10^{-10}$\\ \\ 1. $613453 \cdot 10^{84} = 6.13453 \cdot 10^{89}$ ++++ </nodisp> </box> ==== Binäre Gleitkommazahlen ==== Eine **Gleitkomma-Dezimalzahl** kann man wie folgt darstellen: $$\color{blue}{42}.\color{blue}{13}_\color{magenta}{10}= \color{blue}{4} \cdot \color{magenta}{10}^\color{green}{1} + \color{blue}{2} \cdot \color{magenta}{10}^\color{green}{0} + \color{blue}{1} \cdot \color{magenta}{10}^\color{green}{-1} + \color{blue}{3} \cdot \color{magenta}{10}^\color{green}{-2}$$ Gleichermassen kann eine **Gleitkomma-Binärzahl** dargestellt werden: $$\color{blue}{101}.\color{blue}{11}_\color{magenta}{2}= \color{blue}{1} \cdot \color{magenta}{2}^\color{green}{2} + \color{blue}{0} \cdot \color{magenta}{2}^\color{green}{1} + \color{blue}{1} \cdot \color{magenta}{2}^\color{green}{0} + \color{blue}{1} \cdot \color{magenta}{2}^\color{green}{-1} + \color{blue}{1} \cdot \color{magenta}{2}^\color{green}{-2} $$ Um eine Binärzahl mit Nachkommastellen im Speicher eines Computers zu speichern, bringt man diese zuerst in die wissenschaftliche Schreibweise. Im Speicher werden dann **Vorzeichen, Mantisse und Exponent einzeln gespeichert**. Dabei ist ein Bit für das Vorzeichen und jeweils eine *feste Anzahl Bits* für Mantisse und Exponent festgelegt. Aus dieser Information kann dann die Zahl rekonstruiert werden. Die wissenschaftliche Schreibweise für Binärzahlen funktioniert analog zu der für Dezimalzahlen. Beachte, dass in der Mantisse die Ziffer vor dem Punkt *eine Eins* sein muss. Die einzige Ausnahme ist die Zahl $0_2$. Beim Speichern einer Zahl mit Nachkommastellen wird deshalb diese erste $1$ *weggelassen* - so spart man sich ein Bit! Man sagt, dass die Mantisse **normalisiert** wird: In Exponenten möchte man **negative Zahlen verhindern**. Man addiert zum Exponenten deshalb einen sogenannten **Bias**. Dieser Bias-behaftete Exponent nimmt dann im Normalfall Werte zwischen $1$ und $2^x-2$ an, wobei $x$ die Anzahl Bits für den Exponenten ist. Damit kann man Exponenten zwischen $-(2^{x-1}-2)$ und $+2^{x-1}-1$ darstellen. Die beiden Sonderfälle, wo der Bias-behaftete Exponent $0$ und $2^x$ ist, werden in der Tabelle erklärt: ^**Exponent** ^ **Mantisse** ^ **Beschreibung** ^ |$E = 0$ | $M = 0$ | Zahl $0$ | |$E = 0$ | $M > 0$ | denormalisierte Zahl (für extrem kleine Zahlen) | |$2^x-1 > E > 0$ | $M \geq 0$ | normalisierte Zahl (Normalfall) | |$E = 2^x-1$ | $M = 0$ | Unendlich | |$E = 2^x-1$ | $M > 0$ | keine Zahl / Not A Number (NAN) | Diese Zahlendarstellung wird **Gleitkommadarstellung** genannt, da die Position des Kommas je nach Zahl variiert. Diese Darstellung erlaubt, im Gegensatz zu Darstellungen mit einem festen Platz für das Komma, dass man sowohl sehr grosse wie auch sehr kleine Zahlen mit hoher Genauigkeit speichern kann. Die hier beschriebene Darstellung wird durch die Norm **Norm IEEE 754** festgelegt. <box 100%> **Aufgabe:** Rechne die Binärzahl $101.01_2$ ins Dezimalsystem um. <nodisp 1> ++++Lösung| $$101.01_2 = 1 \cdot 2^{2}+ 0 \cdot 2^{1}+ 1 \cdot 2^{0}+ 0 \cdot 2^{-1}+ 1 \cdot 2^{-2}= 5.25_{10}$$ ++++ </nodisp> </box> <box 100%> **Aufgabe:** Ziel ist es, die Zahl $101.01_2$ als **16-Bit Gleitkommazahl** darzustellen. Dabei sind folgende Anzahl Bits reserviert für: {{ :ef_informatik:gleitkommazahlen_aufgabe.png?600 |}} 1. Notiere die Zahl zuerst in der **wissenschaftlichen Schreibweise** für Binärzahlen.\\ \\ 1. Bestimme (jeweils als Binärzahl): **Vorzeichen, Exponent, Mantisse**\\ \\ 1. Welche Werte sollen der minimale und maximale Exponent haben können? Bestimme den passenden **Bias**, den man zum Exponenten hinzuaddiert.\\ \\ 1. Normalisiere nun die Mantisse und addiere den Bias zum Exponenten. Notiere: Vorzeichen, Exponent mit Bias, Mantisse.\\ \\ 1. Notiere nun die **fertige Gleitkommazahl**, so wie sie im Computer gespeichert wird.\\ \\ 1. Wie sehen die Sonderfälle (siehe Tabelle oben) für eine 16-Bit Gleitkommazahl aus?\\ \\ <nodisp 1> ++++Lösung| 1. $$101.01\_2 = \color{red}{1.0101}\_2 \cdot 2^{\color{green}{2}\_{10}} = \color{red}{1.0101}\_2 \cdot 2^{\color{green}{10}\_2}$$ \\ \\ 1. Vorzeichen: $+$, also $0\_2$\\ Exponent: $10\_2$\\ Mantisse: $1.0101\_2$\\ \\ 1. Da $5$ Bit für Exponenten: Mit 5 Bit gibt $2^5 = 32$ Möglichkeiten, tiefster und höchster Exponent sind reserviert für Sonderfälle (siehe Tabelle), es bleiben also $2^5 - 2 = 30$ Möglichkeiten. Regulärer Exponent geht damit von $-14 \leftrightarrow +15$. Bias muss also $+15 = 01111\_2$ sein und nicht $14\_{10}$, da der Bias-behaftete Exponent der kleinsten normalen Zahl $00001\_2$ ist und nicht $00000\_2$, da dieser Exponent die Zahl $0$ (oder eine denormalisierte Zahl) darstellt. 1. minimaler Exponent: $-(2^{5-1}-2) = -14$ 1. maximaler Exponent: $2^{5-1}-1 = 15$ 1. Bias: $B = 15\_{10} = 01111\_2$ 1. Vorzeichen: $0\_2$\\ Exponent mit Bias: $10\_2 + 01111\_2 = 10001\_2$\\ Mantisse: $0101000000\_2$ 1. $$0\,10001\,0101000000$$ 1. Tabelle, wobei $x=5$ (da $5$ Bits für Exponenten):<WRAP> ^**Exponent** ^ **Mantisse** ^ **Beschreibung** ^ |$E = 0$ | $M = 0$ | Zahl $0$ | |$E = 0$ | $M > 0$ | denormalisierte Zahl (für extrem kleine Zahlen) | |$31 > E > 0$ d.h. $30 \geq E \geq 1$ | $M \geq 0$ | normalisierte Zahl (Normalfall) | |$E = 31$ | $M = 0$ | Unendlich | |$E = 31$ | $M > 0$ | keine Zahl / Not A Number (NAN) | </WRAP> ++++ </nodisp> </box> <box 100%> **Aufgabe:** Wir betrachten wieder 16-Bit Gleitkommazahlen mit 5-Bit Exponent und 10-Bit Mantisse.\\ \\ 1. Bestimme die grösste und zweitgrösste Zahl, die man damit darstellen kann als Dezimalzahl. Was fällt dir auf?\\ \\ 1. Bestimme die kleinste positive und von Null verschiedene Gleitkommazahl, die man als normalisierte Zahl darstellen kann. Rechne sie in eine Dezimalzahl um.\\ \\ 1. Welche Schlüsse ziehst du daraus, wenn es um die Verwendung von ganzen Zahlen und Gleitkommazahlen in der Programmierung geht? <nodisp 1> ++++Lösung| 1. grösste Zahl: $$0\,11110\,1111111111 = 1.1111111111\_2 \cdot 2^{15\_{10}} = 2^{15} + 2^{14} + \ldots + 2^6 + 2^5 = 65504$$ zweitgrösste Zahl: $$0\,11110\,1111111110 = 1.1111111110\_2 \cdot 2^{15\_{10}} = 2^{15} + 2^{14} + \ldots + 2^6 + 0 \times 2^5 = 65472$$ Beachte: Es gibt zwischen diesen Zahlen einen beachtlichen Bereich, der NICHT dargestellt werden kann als $16-$Bit Zahl, zum Beispiel die Zahl $65500$\\ \\ 1. Kleinste positive normalisierte Zahl: $$0\,00001\,0000000000 = 1.0000000000\_2 \cdot 2^{-14\_{10}} = 2^{-14} \approx 6.103 \cdot 10^{-5}$$ Zweitkleinste positive normalisierte Zahl: $$0\,00001\,0000000001 = 1.0000000001\_2 \cdot 2^{-14\_{10}} = 2^{-14} + 2^{-24} \approx 6.109 \cdot 10^{-5}$$ 1. Gute Faustregel: Wenn man etwas mit ganzen Zahlen umsetzen kann, sollte man dies tun. Beispiel: Code für Stoppuhr. Die Zeit könnte man in Sekunden in Gleitkommazahlen speichern. Wahrscheinlich ist es aber besser, die Zeit in Millisekunden in ganzen Zahlen zu speichern. Auf Arduinos sollte man, wenn möglich, Gleitkommazahlen (floats) komplett vermeiden, da diese nicht nativ unterstützt werden. ++++ </nodisp> </box> <box 100%> **Aufgabe:** Für eine Gleitkommazahl stehen insgesamt $n$ Bits zur den Exponenten zur Verfügung. Stelle eine Formel auf oder gib ein Rezept an, wie man den zugehörigen Bias berechnet. <nodisp 1> ++++Lösung| $$B = 2^{n-1}-1 = 011\ldots1_2$$ ++++ </nodisp> </box> <box 100%> <color magenta>**Programmieraufgabe:**</color> Schreibe eine Funktion (Empfehlung: Python), welcher man eine Binärzahl als String (z.B. "101.01"), sowie die Anzahl Bits für Exponent und Mantisse übergibt. Die Funktion bestimmt dann die passende Mantisse und Exponent (inkl. Bias) für die Zahl und gibt diese zurück. Auch der Spezialfall $0$ soll richtig gehandhabt werden. Die anderen Spezialfälle können ignoriert werden. <nodisp 1> ++++Lösung| <code python> def binary_string_to_float(bin_str,len_exp=5,len_mant=10): # SIGN sign = '0' if bin_str[0] == '-': sign = '1' # SPECIAL CASE: ZERO if '1' not in bin_str: return [sign,'0'*len_exp,'0'*len_mant] # TODO: other special cases (too small, too big, nan) # if int convert into float if not '.' in bin_str: bin_str += '.' # EXPONENT, MANTISSA bin_str = bin_str.lstrip('-').lstrip('0') # remove unnecessary stuff bias = (2**len_exp - 2)//2 mantissa = bin_str # determine exponent i_one = bin_str.find('1') # index of first '1' from left i_point = bin_str.find('.') # index of decimal point exponent = i_point - i_one if exponent > 0: exponent -= 1 # remove point from mantissa if '.' in bin_str: mantissa = bin_str[0:i_point] + bin_str[i_point+1::] # add bias to exponent exp_w_bias = bin(exponent+bias)[2:] exp_w_bias = '0' * (len_exp - len(exp_w_bias)) + exp_w_bias if len(exp_w_bias) > len_exp: return [sign,'1'*len_exp,'0'*len_mant] # normalize mantissa mantissa = mantissa.lstrip('0') mantissa_normalized = mantissa[1::] # remove first '1' (i.e. normalize) # ensure mantissa has correct length if len(mantissa_normalized) > len_mant: mantissa_normalized = mantissa_normalized[0:len_mant] else: mantissa_normalized += '0' * (len_mant - len(mantissa_normalized)) # TODO: round mantissa correctly return [sign,exp_w_bias,mantissa_normalized] </code> ++++ </nodisp> </box> ==== Dezimalzahlen mit Nachkommastellen ==== Aus dem Grundlagenfach kennen wir den **Restwertalgorithmus**, mit dem wir ganze Dezimalzahlen (z.B. $132$) ins Binärsystem umrechnen: Wir *dividieren* immer durch $2$ und fügen dem Rest der Binärzahl hinzu. Wie sieht es aber aus, wenn wir die eine Dezimalzahl mit Nachkommastellen ins Binärsystem umrechnen wollen? Zum Beispiel gilt: $$0.5_{10} = \left(\frac12\right)_{10} = 0.1_2$$ $$0.25_{10} = \left(\frac14\right)_{10} = 0.01_2$$ $$0.125_{10} = \left(\frac18\right)_{10} = 0.001_2$$ Der Algorithmus zur Umrechnung von dezimalen Nachkommastellen ins Binärsystem geht ganz ähnlich wie oben beschrieben, nur **multiplizieren** wir immer mit $2$. Die Stelle vor dem Komma wird der Binärzahl angehängt, der Rest geht in die nächste Runde bis irgendwann der Rest $0$ erreicht wird. Dann bricht man ab. Viele Zahlen produzieren aber sich wiederholende binäre Nachkommastellen. **Beispiel:** $0.375$ ins Binärsystem umwandeln: $$0.375 \cdot 2 = \color{blue}{0}.75$$ $$0.75 \cdot 2 = \color{blue}{1}.5$$ $$0.5 \cdot 2 = \color{blue}{1}.\color{red}{0}$$ Es gilt also: $$0.375_{10} = 0.\color{blue}{011}_2$$ Für Zahlen wie $42.375$ gehen wir wie folgt vor: Wir wandeln den Teil vor und den Teil nach dem Komma separat um und fügen sie dann zusammen: * $42\_{10} = 101010\_2$ * $0.375_{10} = 0.011\_2$ Es gilt also $$42.375_{10} = 101010.011_2$$ <box 100%> **Aufgabe:** Wandle wie angegeben um: 1. Dezimalbruch $\cfrac{1}{16}$ als Binärzahl 1. Dezimalzahl $0.5625$ als Binärzahl 1. Dezimalzahl $90.25$ als Binärzahl 1. Dezimalzahl $0.42$ als Binärzahl ++++Lösung| 1. $0.0001\_2$ weil $\cfrac{1}{16} = 2^{-4}$ 1. $0.1001\_2$ 1. $1011010.01\_2$ 1. $.0110101110\ldots\_2$ (geht noch weiter, nach 10 NKS abgebrochen) ++++ </box> <box 100%> **Aufgabe:** Stelle -25.890625 als binäre 16-Bit float dar <nodisp 1> ++++Lösung| * Vorzeichen negativ, also $0$\\ \\ * Vorkommastellen: $25\_{10} = 11001\_2$\\ \\ * Nachkommastellen: $0.890625\_{10} = 0.111001\_2$\\ \\ * ganze Binärzahl: $11001.111001\_2$ \\ \\ * wissenschaftliche Schreibweise: $1.1001111001\_2 \cdot 2^4 $ \\ \\ * Exponent: * Exponent ohne Bias: $4\_{10} = 00100\_2$ * Bias ist: $15\_{10} = 01111\_2$ * Exponent mit Bias: $19\_{10} = 10011\_2$ \\ \\ * Mantisse: * Mantisse nicht normalisiert: $11001111001\_2$ * Mantisse normalisiert: $1001111001\_2$\\ \\ * Zahl als float also: $$1100111001111001_2$$\\ \\ ++++ </nodisp> </box> <box 100%> <color magenta>**Programmieraufgabe:**</color> Schreibe eine Funktion z.B. in Python, mit der man ... 1. Ganze Dezimalzahlen ins Binärsystem umrechnet. Repetition, implementiere selbst den Restwertalgorithmus und verwende nicht z.B. `bin(...)`.\\ \\ 1. Kleine Dezimalzahlen ($0 < a < 1$, haben also Form $0.<\text{nachkommastellen}>$) wie z.B. $0.375$ ins Binärsystem umrechnet. Achtung: Die Funktion soll ein Parameter `precision` haben, mit der man angeben kann, nach wie vielen binären Nachkommastellen abgebrochen werden soll.\\ \\ 1. Beliebige Dezimalzahlen, z.B. $42.375$ ins Binärsystem umrechnen kann. Tipp: Rufe darin einfach die beiden Funktionen aus 1. und 2. auf und schon ist man (fast) fertig! ++++Lösung| ++++ </box> ===== Weitere Aufgaben ===== <box 100%> **Aufgabe:** Warum benötigt man für die Darstellung von 0 eine definierte Ausnahme? Ergeben alle Bits 0 nicht sowieso 0? <nodisp 1> ++++Lösung| Umrechnung ohne die festgelegten Spezialfälle: * Vorzeichen: $0$, also positiv\\ \\ * Exponent: * Exponent mit Bias: $00000\_2 = 0$ * Bias ist: $01111\_2 = 15$ * Exponent ohne Bias: $-15$ \\ \\ * Mantisse: * Mantisse normalisiert: $0000000000\_2$ * Mantisse nicht normalisiert: $10000000000\_2$\\ \\ * Zahl als Binärzahl also: $$+1.0000000000\_2 \cdot 2^{00000\_2}$$\\ \\ * das ist zwar klein, aber immer noch viel zu gross... ++++ </nodisp> </box> <box 100%> **Aufgabe:** Welche Zahl wird hier als Gleitkommazahl (16-Bit mit 5-Bit Exponent und 10-Bit Mantisse) gespeichert: $$1\,10010\,1011010000$$ Bestimme als Binär- und Dezimalzahl. <nodisp 1> ++++Lösung| * Vorzeichen: $1$, also negativ\\ \\ * Exponent: * Exponent mit Bias: $10010\_2 = 18$ * Bias ist: $01111\_2 = 15$ * Exponent ohne Bias: $3$ \\ \\ * Mantisse: * Mantisse normalisiert: $1011010000\_2$ * Mantisse nicht normalisiert: $11011010000\_2$\\ \\ * Zahl als Binärzahl also: $$-1101.101\_2$$\\ \\ * Zahl als Dezimalzahl:$$-1\cdot 2^3 + 1\cdot 2^2 + 0\cdot 2^1 + 1\cdot 2^0 + 1\cdot 2^{-1} + 0\cdot 2^{-2} + 1\cdot 2^{-3} = -13.625$$ ++++ </nodisp> </box> <box 100%> **Aufgabe:** Warum haben die folgenden beiden Zahlen gemeinsam? <code> int i = 64237 float f = -56736f </code> <nodisp 1> ++++Lösung| Beide haben die gleiche Binärrepräsentation $$1111 1010 1110 1101$$ ++++ </nodisp> </box> <box 100%> **Aufgabe:** Stelle die Binärzahl 0.00001001001001001 als 16-bit float dar. Probleme? - Stelle das Ergebnis wieder als Binärzahl dar. Was könnte man tun, um das Problem zu vermeiden? <nodisp 1> ++++Lösung| * wissenschaftliche Schreibweise: $1.001001001001\_2 \cdot 2^{-5} $ \\ \\ * Exponent: * Exponent ohne Bias: $-5\_{10} = 11011\_2$ * Bias ist: $15\_{10} = 01111\_2$ * Exponent mit Bias: $19\_{10} = 01010\_2$ \\ \\ * Mantisse: * Mantisse nicht normalisiert: $1001001001001\_2$ * Mantisse normalisiert: $001001001001\_2$\\ \\ * Zahl als float also: $$001010001001001001_2$$\\ \\ * <color red>oder doch nicht?!? Und jetzt?</color> ++++ </nodisp> </box> <box 100%> **Aufgabe:** Addiere die beiden folgenden float-Zahlen $$0011001011001000$$ $$0010101001100111$$ <nodisp 1> ++++Tipps| * Exponenten ermitteln * Mantissen ermitteln * Exponenten angleichen, Komma der Mantissen entsprechend verschieben. Bias beibehalten. * Addition durchführen * Komma verschieben, Exponent angleichen * Runden * Zusammensetzen ++++ </nodisp> </box> ==== Zusatzaufgaben: Programmieren ==== <box 100%> **Aufgabe:** 1. Bestimme die grösste und zweitgrösste Zahl, die man in einem C\#-Float (8-Bit Exponent, 23-Bit Mantisse) speichern kann. Verwende Python.\\ \\ 1. Wie gross ist die Differenz? Verwende Python.\\ \\ 1. Überzeuge dich davon, dass C\# nicht zwischen der grössten Zahl und zwischen einer Zahl, die in diesem Gap liegt, unterscheiden kann, zum Beispiel indem du diese von einander subtrahierst. Python hingegen hat da keine Probleme.\\ Verwende z.B. den folgenden C\#-Online-Editor: https://replit.com/languages/csharp <nodisp 1> ++++Lösung| 1. Grösste Zahl: $340282346638528859811704183484516925440 \approx 3.4 \cdot 10^{38}$\\ Zweitgrösste Zahl: $340282326356119256160033759537265639424$\\ \\ 1. Differenz: $20282409603651670423947251286016 \approx 2 \cdot 10^{31}$, also eine riesige Differenz\\ \\ 1. Man erhält immer $0$, z.B. ergibt folgende Subtraktion $0$: $$340282346638528859811704183484516925440\text{f} - 340282340000000000000000000000000000000\text{f}$$ ++++ </nodisp> </box> <box 100%> **Aufgabe:** Führe mithilfe von Floats die triviale Berechnung $3.4-3.3$ aus. Was fällt dir auf? Verwende eine typsichere Programmiersprache wie C#. Hier findest du Online-Editoren: * C#: https://replit.com/languages/csharp <nodisp 1> ++++Lösung| Resultat ist $0.1000001$. Also nicht exakt, Grund: Rundungsfehler. $3.3$ und $3.4$ haben als Binärzahl ausgedrückt jeweils unendliche viele sich periodisch wiederholende Nachkommastellen. Diese müssen gerundet werden, was zu diesem Fehler führt. ++++ </nodisp> </box> ==== Zusatzaufgaben: Mathematik ==== <box 100%> **Aufgabe:** Schätze die Grössenordnung der grössten Zahl, die ein C#-Double (11-Bit Exponent, 52-Bit Mantisse) darstellen kann, ab. Alles was du dazu verwenden darft, ist dein Kopf, also keinen Computer. Taschenrechner, Computer usw. sind tabu! Berechne dazu die Potenz, die aus dem MSB (most significant bit, also dem Bit, welches ganz links steht) heraus geht, was $2$ hoch `eine grosse Zahl' ist, was eine sehr grosse Dezimalzahl ergibt. Ein handelsüblicher Taschenrechner ist nicht in der Lage, diese Zahl zu berechnen. Der Trick hier ist, dass du dein Wissen über Logarithmen verwendest. Dann schaffst du diese Rechnung ganz ohne Rechner. Insbesondere benötigst du die Formel für den Basiswechsel $$\log_a k = \frac{\log_b k}{\log_b a} \,.$$ <nodisp 1> ++++Lösung| Muss Grössenordnung von $2^{1023}$ abschätzen. Grösste Zahl ist $1.798 \cdot 10^{308}$. Mit Abschätzung ohne technische Hilfe sollte man auf etwa $10^{310}$ kommen. ++++ </nodisp> </box> <box 100%> **Aufgabe:** Beweise, dass es im Interval $[0,1]$ überabzählbar unendlich viele reellen Zahlen gibt. </box> ef_informatik/zahlensysteme.txt Zuletzt geändert: 2024-08-27 12:40von sps