Schönen guten Nachmittag.
Herzlich willkommen zur Programmiervorlesung.
Was wir das letzte Mal gesehen hatten, kurze Erinnerung, war ein ganz
einfaches Programm.
Wir hatten gesehen, ein Programm, was Zahlen aufaddiert.
Wir hatten auch gelernt, vielleicht für einige doch ganz erhellend,
wenn man erstmal über die Mathematik sich Gedanken macht, merkt man,
dass man das Programm signifikant vereinfachen kann.
Wir hatten diese Gauss'sche Summenformel angewandt.
Sie haben gesehen, man muss ein Programm erstmal kompilieren, dann
ausführen.
Das hatten wir uns angeschaut.
Wir hatten uns auch ein bisschen die Hintergründe angeschaut dazu.
Und tatsächlich hatten wir auch schon die ersten Objekte und Klassen
gesehen.
Und das ist natürlich der Einstieg in eine objektorientierte
Programmiersprache wie Java.
Wir haben gesehen, diese Klassen haben zwei Eigenschaften.
Einmal Attribute, das sind Variablen.
Da reden wir heute noch längere Zeit drüber.
Und sie haben Codeschnipsel, den sogenannten Methoden.
Das sind Dinge, die man ausführen kann und die auf diesen Variablen,
auf diesen Daten arbeiten.
Und sie hatten gesehen, wie man eine Klasse deklariert.
Das war unsere Vector2D-Klasse.
Und das Billardspiel.
Und wir hatten da auch einige Objekte von erzeugt.
Verschiedene Billardkugeln zum Beispiel.
Wenn man den Stoff der Vorlesung jetzt im Überblick sich anschaut,
dann sehen Sie in diesem Diagramm, das ist das, worüber wir heute
reden, über Datentypen.
Was sind primitive Datentypen?
Was sind deren Wertebereiche?
Wir werden auch kurz über Klassen sprechen und nochmal, wie man die
erzeugt.
Also die sogenannten Instanzen von der Klasse.
Das sind die Objekte.
Aber das Ganze hängt natürlich auch zusammen mit weiteren Inhalten der
Vorlesung, die dann später kommen.
Wie baut man ganze Systeme auf?
Was sind also jetzt erweiterte Datentypen, zusammengesetzte
Datentypen, Arrays, Listen?
Wie baut man sinnvoll Klassenhierarchien auf?
Und was gibt es hier noch für weitere Elemente wie Schnittstellen oder
generische Klassen?
Das kommt aber alles später im Verlauf der Vorlesung.
Und dann werden wir natürlich auch nochmal etwas über Methodik
sprechen.
Banale Dinge wie Ein- und Ausgabe.
Aber auch, wie gehe ich vor, zum Beispiel um Fehler zu finden?
Wie baue ich Testfälle auf?
Was gibt es für Coding-Conventions?
Das kommt immer so nebenbei mit.
Und was sind allgemein auch Best Practices?
Also guter Stil beim Programmieren.
Dinge, die man machen sollte, Dinge, die man vermeiden sollte.
Das wird auch kontinuierlich in der ganzen Vorlesungsserie Inhalt
sein.
Heute geht es darum, dass wir uns sogenannte Datentypen in Variablen
anschauen.
Und zwar die, die schon mal fest eingebaut sind in Java.
Und Sie wissen also nachher, was diese Datentypen sind und auch, was
sind die Operationen, die man auf diesen Datentypen anwenden kann.
Sprich, mit denen man mit diesen Datentypen neue Dinge berechnen kann.
Und wir sprechen dann über, wenn man so will, die Instanzen von den
Typen.
Das sind die sogenannten Variablen.
Das sind im Prinzip Namen für Speicherbereiche.
Und die haben Werte.
Das können Sie durch eine Zuweisung verändern, den Wert.
Und Sie wissen nachher auch, was jetzt Objektvariablen sind und was
eine Objektreferenz ist.
So.
Es gibt diesen alten Witz, Programmieren ist so ein bisschen wie
Schriftstellerei.
Man denkt sich erst ein paar Typen aus und muss nachher damit
klarkommen.
Und genauso ist es im Prinzip auch.
Sie können auch nachher selber Typen definieren.
Damit man aber überhaupt erstmal einen Startpunkt hat, um eigene Typen
zu definieren, gibt es eben schon mal fest eingebaute in der
Programmiersprache.
Und die kommen heute als erstes Mal dran.
Erstmal abstrakt gesprochen.
Was ist so ein Typ?
Sie können sich einen Typ eigentlich erst mal vorstellen als eine
Menge möglicher gleichartiger Werte.
Was ist mit gleichartig gemeint?
Mit gleichartig ist gemeint, dass Sie zum Beispiel sagen, der Typ der
ganzzahligen Zahlen ist eine Menge der ganzzahligen Zahlen.
1, 2, 3, 4, 5.
Aber da ist nicht 1, 2, 3 a, b, 7, 5, 3 pi drin.
Das wäre nicht gleichartig.
Sondern wenn dann zum Beispiel die ganzen Zahlen oder Buchstaben.
Aber eben, das ist mit gleichartig gemeint.
Nicht Dinge ineinander nochmal gemischt.
Diese Datentypen legen letzten Endes fest, welche möglichen Werte kann
eine Instanz von dem Datentyp, die Variable, haben.
Das ist dann einer dieser Werte aus dieser Menge von Werten.
Und Sie ahnen es, der Computer ist endlich groß.
Das heißt, diese ganzen Wertebereiche sind auch immer nur endlich.
Das heißt, wenn wir sagen, wir sprechen von einem Typ der ganzzahligen
Zahlen, dann ist das eigentlich leider nur eine sehr schlechte
Approximation.
Die ist nämlich bei uns am Computer abgeschnitten.
Das sind natürlich nicht unendlich viele, das geht nicht.
Dasselbe gilt auch, wenn wir über Gleitkommazahlen sprechen.
Da werden wir noch eine längere Zeit heute drüber sprechen.
Auch da werden Sie sehen, das ist nur eine sehr grobe Approximation
der reellen Zahlen.
Aber auch die reellen Zahlen sind ja noch schlimmer als die
natürlichen Zahlen.
Überabteilbar und endlich kriegen wir natürlich gar nicht in den
Computer rein, brauchen wir also auch nur eine Approximation.
Diese Datentypen haben eine Reihe von Vorteilen.
Es gibt noch Programmiersprachen, die haben keine Datentypen.
Aber wenn man Datentypen hat, hat man zum einen den Vorteil, dass
bestimmte Fehlerarten schon automatisch vom Compiler, also beim
Übersetzen des Programmes, identifiziert werden können und Ihnen
zurückgemeldet werden können.
Jetzt können Sie sagen, ein Fehler ist schlimm genug.
Was ist jetzt der Unterschied, ob der Compiler mir das zurückliefert
oder nachher im Betrieb das zurückkommt?
Naja, der Unterschied ist relativ gravierend.
Erstens, weil der Compiler entdeckt alle Datentypenfehler, egal wie
das Programm nachher ausgeführt wird.
Das heißt, Sie haben nachher eine Garantie, wenn es durch den Compiler
läuft, dass Datentypenfehler nicht mehr drin sind.
Wohingegen, wenn das Programm später ausgeführt würde, kann es ja nur
sein, dass der Fehler gerade nicht aufgetreten ist, weil man die
Stelle im Programm nicht verwendet hat, aber der Fehler ist trotzdem
noch drin.
Das andere ist, wenn Sie das Programm als Entwickler kompilieren und
eine Rückmeldung über die Fehler bekommen, können Sie den Fehler auch
versuchen zu finden und zu korrigieren.
Wenn der Fehler im Betrieb auftaucht, können die Auswirkungen
natürlich deutlich schlimmer sein.
Und für Sie ist es natürlich auch deutlich schwieriger, den Fehler
dann zu identifizieren und rauszufinden.
Möglicherweise ist der original Entwickler gar nicht mehr da und
andere müssen den Fehler finden.
Auch schwieriger.
Das heißt natürlich, je früher man Fehler findet, desto besser.
Und dass der Compiler einem Rückmeldung über Fehler gibt, auch eine
super Sache.
Das andere, das ist ein Argument, was mittlerweile nicht mehr ganz so
stimmt, aber zumindest mal früher stimmte, ist, es hilft auch dem
Compiler, guten Code zu generieren, wenn er weiß, was die Typen sind.
Das stimmt deswegen nicht mehr ganz, für die Fortgeschrittenen war das
so ein Steinkommentar, weil moderne Programmiersprachen können auch
die Typen inferieren, selber logisch schließen aus dem Programmtext,
ohne dass sie explizit mal deklariert wurden.
Das heißt also mit anderen Worten, der Vorteil ist letzten Endes
mittlerweile nicht mehr da.
Nichtsdestotrotz, die Datentypen bestimmen auch, welche Operationen
auf den Werten möglich sind.
Das leuchtet Ihnen relativ schnell ein.
Sie können zum Beispiel auf Zahlen addieren, Sie können Zahlen auf
Größer -Gleich vergleichen, Sie können aber zum Beispiel Zeichenketten
nicht so leicht, ohne eine weitere Definition, auf Größer- oder Gleich
vergleichen.
Das geht halt einfach nicht, für diesen Typ der Zeichenkette.
In Java habe ich erstmal acht elementare Datentypen.
Elementar kann man dadurch übersetzen, die sind nicht zusammengesetzt
weiter und die sind quasi eingebaut in die Sprache.
Das sind eine Approximation der ganzen Zahlen, der Fließkomma-Zahlen,
das sind Wahrheitswerte, wahr und falsch und Zeichen und
Zeichenketten.
Aber vielleicht erinnern Sie sich noch in der letzten Vorlesung, da
hatte ich auch gesagt, Sie instanziieren von einer Klasse ein Objekt.
Und genau das macht auch Sinn, genauso wie Sie von einem Typ eine
Variable instanziieren.
Es ist tatsächlich so, jede Klasse bildet auch einen Typ.
Und zwar der Typ überlegt, was ist das?
Typ ist eine Menge von Werten.
Typ ist dann die Klasse als Typ, die Menge der möglichen Objekte mit
ihren möglichen Attributwertbelegungen ihren Attributbelegungen bildet
quasi den Typ einer Klasse.
So.
Schauen wir uns mal diese Datentypen in Java an.
Das sind jetzt auch die Wörter, wie sie in der Java-Programmiersprache
verwendet werden, um diese Typen zu beschreiben.
Boolean, nach einem Engländer George Boole benannt.
Wichtig, das ist Boole mit E am Ende.
Das sind Wahrheitswerte, wahr und falsch.
Wie wir damit rechnen können, sehen wir später noch.
Dann Char, Character, steht für 16-Bit-Unicode-Zeichen in Java für die
C++ -C-Programmierer ist die Welt noch ein bisschen anders.
Und Unicode-Zeichen für die, die das Wort Unicode nicht kennen, ist
nicht schlimm.
Unicode ist im Prinzip, wenn Sie sich das vorstellen, eine große
Tabelle, wo drinsteht, wie ein darstellbares Zeichen als Zahl
repräsentiert wird.
Alle Buchstaben sind da drin, aber jede Menge auch Sonderzeichen.
Dinge, die nicht auch klassischerweise im lateinischen Alphabet drin
sind.
Und diese Tabelle gibt für alles einen Zahlenwert an.
Man kann aber auch jetzt hier durch diese einfachen Apostrophs sagen,
also das soll ein Buchstabe A sein, oder das soll ein Carriage Return
sein, also ein Zeilenumbruch.
Oder das soll das Zeichen sein, was den Hexadezimalwert 05 0D hat.
Und das Backslash U heißt, jetzt kommt ein Hexadezimalwert.
Also ein Wert im Zahlensystem mit der 16er-Basis.
Despite ist eine 8-Bit-Ganzzahl.
Short ist eine 16-Bit-Ganzzahl.
Int ist eine 32-Bit-Ganzzahl.
Und Long ist eine 64-Bit-Ganzzahl.
Ich mache gerade eine kleine Frage an Sie.
Wer hat schon mal CC++ programmiert?
Genau, das sind ja doch einige.
So ein Drittel würde ich mal abschätzen.
Die kennen natürlich diese Wörter.
Byte, Short, Int, Long.
Sorry, Character.
Nur, ihr werdet es merken, hier in Java ist es fest definiert, dass
zum Beispiel ein Short schon 16-Bit sind und ein Int 32-Bit und ein
Long 64-Bit.
In C ist das von der Plattform abhängig.
Das ist nicht fest definiert.
Aber wie gesagt, hier in Java ist das Plattformunabhängiger, es ist
fest definiert.
In C gibt es durchaus auch Systeme, wo ein Short 8-Bit sind.
Das erschwert natürlich die Portabilität der Programme von einer
Plattform auf die anderen.
Aber hier in Java ist das alles fest definiert.
Dann gibt es noch Float, das steht für Gleitkommazahl, Fließkommazahl.
Daher dieses Wort Float.
Wie gesagt, man kann sich in erster Näherung vorstellen, das ist sowas
wie Kommazahlen.
Rationale Zahlen, reelle Zahlen.
Stimmt aber nicht ganz.
Das werden wir uns gleich noch im Detail anschauen.
Das ist eine sehr grobe Approximation der Sache.
Und Double ist quasi das Snappy wie Float, nur mit doppelter
Genauigkeit.
Deswegen Double für doppelte Genauigkeit, Gleitkommazahl.
So, wie ich sagte, jeder Typ gibt letzten Endes eine Menge vor, eine
Menge von möglichen Werten.
Und diese Wertebereiche sind so definiert.
Beim Charakter ist es 0, und zwar der Zahlenwert 0, der kleinste Wert
und der Zahlenwert FF.
Also 4 mal das F für Hexadezimal.
Das ist der höchste Wert, der angegeben werden kann.
Bei Byte, das sind jetzt Ganzzahlen.
Genau, jetzt werden Sie sich fragen, wie komme ich jetzt auf den
Charakter, auf das Zeichen?
Dafür braucht man die Unicode-Tabelle, die quasi diesen Zahlenwert
umwandelt in ein Zeichen.
Wenn Sie sich jetzt wundern, warum stehen jetzt hier vier Zeichen und
nochmal Backslash-U?
Das heißt, wie gesagt, Hexadezimalwert von FFFF.
Und das ist, wie gesagt, eine Zahl.
Genauer gesagt, die hier, 65.535.
Und das können Sie in der Unicode-Tabelle nachschauen, welcher
Charakter das ist.
Bei den Ganzzahlen ist es so, in einem Byte können Sie, das wissen Sie
vielleicht auswendig, 256 verschiedene Werte speichern.
Wenn man das Ganze mit Vorzeichenbehaftung und einer 0 macht, kommt
man von minus 128 plus 127.
Warum nicht bis plus 128?
Naja, weil die 0 ja auch noch ein Wert ist, den ich speichern muss.
So, bei Short ist es ähnlich, nur jetzt wird es schon mit 2 Bytes.
Und deswegen kommt man dann eben in diesen etwas größeren Wertebereich
von minus 32.768 bis plus 32.767.
Auch wieder eins weniger, weil die 0 ja wieder einen eigenen Wert
darstellt.
Und analog geht es jetzt so weiter mit dem Int.
Nochmal jetzt 4 Bytes, 8 Bytes.
Sie sehen, Sie können natürlich immer höhere Ganzzahlen speichern,
aber es ist endlich.
Also irgendwann ist auch Schluss und Sie haben einen Überlauf, das
werden wir uns gleich auch mal anschauen.
Bei den Gleitkommazahlen ist es ungefähr der Wert, der der höchste
ist.
Bei Float 10 mit 38 Nullen ist schon für viele Dinge ausreichend.
Und für Double 10 mit 308 Nullen ist auch ein ziemlich hoher Wert.
Aber Achtung, Achtung, das werden wir uns gleich auch anschauen.
Diese Zahlen bis dahin sind nicht in sehr engen Abständen.
Das heißt, wir werden uns das gleich mal anschauen.
Das sind quasi Lücken im Zahlenstrahl.
Und das macht das Rechnen mit Doubles so ein bisschen gefährlich, weil
man eben sehr schnell sich Rundungsfehler einhandelt.
Sie sind nicht besonders genau.
Die Auflösung ist nicht nur besonders extrem fein, sondern es gibt
auch wirklich Lücken.
Es gibt Zahlen, die können Sie nicht darstellen.
Deswegen ist dieser große Zahlenbereich, den Sie hier sehen, klingt
erstmal toll.
Damit habe ich ja gar keine Sorgen.
Trotzdem sollten Sie, wann immer es reicht, vom Zahlenbereich auf die
Ganzzahlen umgehen, also wenn Sie nicht besonders große Zahlen
brauchen oder wenn Sie nicht eben mit Kommata rechnen müssen, weil
dann können Sie Zahlen genau darstellen.
Wie gesagt, hier unten, das sind immer nur Approximationen und hier
gibt es große Lücken im Zahlenstrahl, die gar nicht darstellbar sind
bis dahin.
So, dann würde ich jetzt davon ausgehen, das kennen Sie von der
Schule.
Ich will es hier nochmal kurz erwähnen.
Es gibt ja nicht nur die Basis 10, mit dem man rechnen kann.
Wir rechnen als Menschen im Zehner-System.
Der Grund ist übrigens extrem banal.
Weiß einer, warum wir Menschen im Zehner-System rechnen?
Ja?
Ganz genau, es ist vollkommen richtig.
Ich wiederhole es nochmal für alle.
Weil wir 10 Finger haben.
Es ist vollkommen banal.
Hätten wir 12 Finger, würden wir wie die Engländer im Zwölfer-System.
Na gut, okay.
Es gibt tatsächlich einige Naturstämme, die rechnen mit 20, weil sie
doch die 10 dazunehmen.
Aber das ist natürlich klar, in Europa, wo es kälter ist, hat das
nicht geklappt.
Deswegen haben wir das Zehner-System.
Das hat sich dann auch weltweit durchgesetzt.
Es gibt beim Zehner-System so eine blöde Eigenschaft als Informatiker.
Und zwar ist es so, Sie wissen ja vielleicht, intern werden alle Werte
im Rechner binär, also mit 0 und 1 gespeichert, was verschiedene
Spannungswerte sind.
Das werden Sie noch im Laufe des Studiums auch in der technischen
Informatik sich genauer anschauen.
Jetzt ist es so, wann der Stellenübertrag kommt vom Binärsystem und
dem Zehner-System, das passt nicht zueinander.
Sie wissen, Sie brauchen für einen Bit können Sie die Werte 0 und 1
darstellen.
Wenn Sie zwei Bits haben, können Sie die Werte 0, 1, 2, 3, 4
darstellen.
Da ist also 0 bis 3 dargestellt.
Für die 4 brauchen Sie eine Stelle wieder mehr.
Das ist dann 1, 0, 0.
Und dieses Anbauen der Stellen haben wir ja im Zehner-System auch.
Vom Übergang von der 9 auf die 10.
Nur dummerweise ist da kein Stellenzuwachs im Binärsystem.
Und das führt dazu, dass man, wenn man im Zehner-System mit aller
Gewalt rechnen wollte und man für jede Ziffer im Zehner-System einfach
die Binärdarstellung nimmt, man immer ziemlich viel verschenkt an
darstellbaren Zahlen.
Weil man halt im Prinzip schon 4 Bits braucht, um die 10 darzustellen.
Aber man könnte noch höher gehen.
Man könnte bis zu 15 gehen.
Die könnte man auch mit 4 Bits darstellen.
Wenn man aber immer eine Dezimalzahl als Binärzahl codiert, einzeln
quasi im Zehner-System, dann würde man halt immer diese letzten Werte
10, 11, 12, 13, 14, 15 nie ausnutzen können.
Weil wir das ja nicht als Ziffer haben im Zehner-System.
Und deswegen macht es auch gar keinen Sinn, im Zehner-System irgendwie
rumzurechnen und das Zehner-System stellenweise auf Binär umzurechnen.
Stattdessen rechnet man dann eben entweder im Binär-System, das ist
aber für uns Menschen ein bisschen unhandsam, weil da sehr lange 0,1
Ketten rauskommen, damit das leichter wird.
Und man trotzdem aber immer den selben Stellenübergang hat wie im
Binär -System.
Hat sich mal das Oktal-System, ich will nicht sagen durchgesetzt, das
wurde mal eingeführt, weil es auch den Vorteil hat, wenn das Oktal
-System einen Stellenübergang, also eine weitere Stelle dazu nimmt,
nimmt auch das Binär-System eine Stelle dazu.
Oder hexadezimal, dass man sagt, ich rechne eben im 16er-System, auch
hier hat man dieselbe Eigenschaft.
Ich brauche immer quasi, wenn ich eine weitere Hexadezimal-Zahl dazu
nehme, habe ich auch wieder einen Stellenzuwachs im Binär-System.
Zwei Hexadezimal-Ziffern sind immer ein Byte.
Also 0xff, also nicht 4xf, 2xf, ist quasi ein Byte, sind 8 Bit.
Und wenn Sie im Prinzip 0xfff haben, dann haben Sie halt zwei Bytes.
Und das ist eigentlich sehr schön, weil das quasi immer zeigt, immer
zwei Hexadezimal-Ziffern im System sind immer ein Byte.
Wenn Sie mehr haben, brauchen Sie entsprechend immer das Byte mehr,
aber Sie haben keinen Verschnitt da drin, wie wenn Sie ein 10er-System
stellenweise umrechnen würden.
Und das ist der Grund, warum dieses Oktal- und Hexadezimal-System
aufgetaucht sind.
Das ist jetzt nicht, weil Informatiker 16 Finger haben, sondern
einfach nur, weil der Stellenübergang eben immer passend auch zum
Binär -System ist, wo ich auch im Binär-System eine weitere Stelle
hinzunehmen muss beim Rechnen.
Und das drückt man so aus, also dieses 0x hat sich so ein bisschen
eingebürgert bei Informatikers.
Damit sagt man, jetzt kommt eine Hexadezimal-Zahl.
Das x vielleicht schon Hexa.
Und das ist ein bisschen schwer erkennbar.
Das hier soll keine 0, sondern ein O sein.
Das soll, wie gesagt, nochmal eine Oktal-Zahl sein.
Da das aber immer schlecht lesbar ist, habe ich das mal hier auch in
Rot gemacht, aber das ist natürlich irgendwie schlecht.
Also da hat man sich schnell vertan, und man sieht den Fehler nicht.
Aber 0 und O sieht natürlich relativ ähnlich aus.
Meiner Erfahrung nach, wenn man aber irgendwie was mal wirklich binär
eingeben will, dann nimmt man das Hexadezimal-System.
Also Oktal habe ich selber auch beim Programmieren eigentlich noch nie
verwendet.
So, wenn man sich jetzt mal anschaut, jetzt habe ich diese
verschiedenen Datentypen, die definieren alle Wertebereiche, die, die
wir eben gesehen haben.
Was kann man jetzt darauf rechnen?
Und wie ich eingangs sagte, die Rechenoperation, die man hat, hängt
natürlich ab, welchen Typ ich habe.
Und wenn man jetzt sagt, ich habe erstmal diese Zahlen hier, dann kann
ich zum Beispiel plus minus X machen, das ist ein näheres, quasi mal
plus 1 oder mal minus 1 multiplizieren.
Multiplizieren, also binäre Operationen, ganzteilige Divisionen,
Modulo -Operationen, wer das noch nicht kennt, Modulo-Operationen ist
quasi der Rest der beider Divisionen.
Da ist also X modulo Y, liest man dieses Prozentzeichen, und das ist
quasi, wenn ich ganzteilig X durch Y definiere, der Rest, der dabei
rauskommt.
Das ist halt 0 oder halt bis Y minus 1 als Wert.
Dann natürlich plus und minus und dann kann ich hier Größenvergleiche
anstellen.
Kleiner, kleiner gleich, größer, größer gleich.
Da ist das Ergebnis aber natürlich nicht eine Zahl, die C
-Programmierer schrecken auf, das ist natürlich auch wieder eine Zahl,
stimmt, aber in Java nicht.
In Java sind das wirklich Wahrheitswerte, nämlich wahr oder falsch ist
das Ergebnis.
Und dasselbe gilt natürlich auch bei dem Vergleich auf Gleichheit.
Das ist dieses Gleich-Gleich und Ausrufezeichen gleich.
Ausrufezeichen ist der Nicht-Operator, der Not-Operator.
Das heißt, es steht also Nicht-Gleich und Gleich-Gleich.
Für diejenigen, die Programmieren schon ein bisschen kennen und so,
die wundern sich an der Stelle gar nicht.
Für die anderen, die jetzt neu sind, denken, Moment mal, Gleich
-Gleich, warum steht da jetzt Gleich-Gleich?
Ist das ein Tippfehler?
Und Nee, Gleich-Gleich ist wirklich der Vergleich von zwei Zahlen.
Das Gleich ist uns in der letzten Vorlesung aufgetaucht, also das
einfache Gleichzeichen, das ist die Zuweisung.
Das heißt, links soll der Wert, der rechts steht, zugewiesen werden.
So was wie x ist gleich 5.
Das vergleicht nicht, da kommt auch nicht wahr oder falsch raus,
sondern das heißt einfach nur, der Wert 5 soll jetzt dieser Variable x
zugewiesen werden.
Das ist aber was anderes, eine Zuweisung.
Hier, das ist wirklich der Vergleich von zwei Sachen.
x hat denselben Wert wie y, deswegen schreibt man hier zweimal gleich
hin.
Wenn ich einmal gleich hinschreiben würde, hätte nachher garantiert x
den Wert von y, weil es eine Zuweisung ist.
Dann gibt es noch Operatoren auf ganzen Zahlen, und zwar dieses tilde
hier, diese Schlange, das ist das bitweise Komplement, also man stellt
sich quasi die Zahl als Binärzahl vor, mit 0 und 1 und invertiert
dann, jede 0 wird eine 1, jede 1 wird eine 0.
Braucht man beim Rechnen in den meisten Fällen nicht.
Wenn man ein bisschen maschinennäher unterwegs ist, braucht man das
vielleicht mal.
Und dann gibt es das bitweise und, das bitweise x or, exklusive oder,
und das bitweise oder.
Dann müssen Sie sich wieder beide Zahlen als in Bitdarstellungen
vorstellen, wir werden uns das mal nachher auch am Beispiel sehen.
Und dann verundert oder verodert man das, also verunden heißt, es
kommt eine 1 raus, wenn beide Werte 1 sind an der Stelle und nur 0
sonst.
Und bei oder heißt es im Prinzip, es kommt eine 0 raus, wenn beide
Werte 0 sind und ansonsten eine 1.
Und beim exklusiven oder ist es so ähnlich wie beim oder, nur wenn
beide Werte wieder 1 haben, zusammen x or sind wieder 0.
x or heißt entweder oder, aber es darf nicht beides wahr sein.
So, und das sind quasi bitweise Operationen mit diesen einzelnen
Zeichen auf den ganzen Zahlen, die ich mir eben bitweise vorstelle.
Jetzt gibt es nochmal das hier auch und und und oder oder Zeichen,
also Strich Strich hier.
Und das ist eine logische Verknüpfung, das ist was anderes.
Eine logische Verknüpfung heißt, ich habe zwei Ausdrücke, die einen
bool'schen Wert, also wahr oder falsch haben können.
Und jetzt kann ich ein logisches und machen, das heißt also, wenn,
wieder wie eben bei den Bits, wenn einer von den beiden den Wert 1
hat, dann ist beim oder eben der Wert auch 1 und beim und ist es so,
dass der Wert 1 nur rauskommt, wenn beide wahr sind.
Das ist aber eine logische Verknüpfung von zwei Bedingungen,
wohingegen das ist das bitweise Rechnen auf Zahlen.
So, und dann gibt es noch, was man aber auch, sage ich mal, nur in
Spezialfällen wirklich braucht und diejenigen, die das nicht sofort
jetzt einsehen, wozu das braucht, nur der Vollständigkeit halber.
Ich nehme jetzt quasi die...
den Wert von x, wieder binär gerechnet, und rücke ihn um y-Stellen
nach links.
Das ist so ein Links-Shift und da gibt es noch ein Rechts-Shift, wo
die Stellen nach rechts gemacht werden.
Und wenn das ganze nicht Vorzeichen beachtet ist und das Vorzeichen
nicht beachtet werden soll, dann macht man das mit 3.
Das ist wie gesagt, Infos gibt es im Anhang vom Foliensatz, werde ich
jetzt nicht darauf eingehen, braucht man auch im Prinzip sehr, sehr
selten, dann guckt man sich das an, aber nur der Vollständigkeit
halber, falls Sie das mal sehen.
So, jetzt ist es...
So, jetzt können wir ja verschachtelte Ausdrücke schreiben, also
Variable mit Operatoren verknüpft.
Hier haben wir so was, also x kleiner 2 mal y plus 1 und es muss auch
gelten, dass nicht y größer gleich...
x größer gleich y modulo z ist.
So, und jetzt wenn man diesen Ausdruck sieht, denkt man so...
Punkt vor Strichrechnung, weiß ich noch aus der Schule, aber wie ist
das mit den anderen Operatoren?
Sprich, man braucht ja jetzt sogenannte Präzedenzregeln, also
Vorrangsregeln auf Deutsch.
In welcher Reihenfolge wertet man so einen Ausdruck aus?
Und jetzt ist es tatsächlich so, dieser Ausdruck wird, wenn man den
vollständig klammern würde, so ausgewertet.
Und das zum Beispiel 2 mal y erstgerechnet und dann plus 1, das wissen
Sie von der Grundschule.
Punkt vor Strichrechnung.
Aber was Sie eben auch sehen und was vielleicht vorher nicht so klar
war, dass dieser Vergleich, der wird erst später gemacht, also es muss
erst dieser Ausdruck ausgerechnet werden und dann kann der Vergleich
mit x gemacht werden.
Macht auch Sinn, wenn man drüber nachdenkt, weil wenn ich jetzt hier x
kleiner 2 auswerte, habe ich hier einen Wert true oder false.
Den kann ich aber nicht mit y multiplizieren, das geht auch nicht.
Und umgedreht auch auf dieser Seite werte ich erst diesen Teil aus y
modulo z und der Wert wird dann geschaut, ob er kleiner gleich x ist.
Und dann wird das Ganze logisch umgedreht.
Das hier kommt dann wahr oder falsch raus, das wird dann umgedreht.
Wahr wird zu falsch, falsch wird zu wahr.
Und dann wird es logisch mit und verknüpft mit dem, was hier raus kam.
So und hier sieht man jetzt die Java Sprachstandard festgelegte
Präzedenztabelle, das heißt also welche Operatoren binden stärker,
welche binden schwächer und im Prinzip die niedrigere Präzedenz heißt,
sie bindet stärker.
Das heißt also dieses plus x minus x, das bezieht sich immer sofort
darauf, auf die Variable dahinter.
Danach kommt Multiplikation, Division und Modulo.
Dann kommt die Strichrechnung, das passt auch zu dem, was Sie in der
Schule gelernt haben.
Dann erst kommen Vergleiche und dann die logische Negierung ist auch
bindet ganz stark hier.
Dann kommt noch das und und oder als Letztes, als Schwächste.
Das müssen Sie jetzt nicht auswendig lernen.
In vielen Fällen erschließt sich das, aber manchmal ist es halt
vielleicht nicht klar und wenn zwei Operationen auf derselben Stufe
stehen, wie jetzt zum Beispiel plus und minus, dann geht man einfach
von links nach rechts, so wie man es lesen würde und werte dann so aus
an der Stelle.
So, die Wahrheitswerte Boolean, die, wie gesagt, ich denke mal, das
kennen Sie wahrscheinlich schon aus der Schule.
Wenn man jetzt A und B hat, also einmal A den Wert 0, B den Wert auch
0 und dann alle vier Kombinationen aufschreibt, dann ist eben A
logisch unverknüpft, dann nur 1, wenn beide Werte 1 sind, oder ist
halt nur dann 0, wenn beide Werte 0 sind und Licht ist genau die
Invertierung und X oder das exklusive oder ist halt nur wenn genau
einer der Werte hier 1 ist, kommt dann hier eine 1 raus und ansonsten
0.
Aber wie gesagt, ich denke mal, das ist das kennen Sie ja.
Und wie gesagt, hier ist jetzt 1 Abkürzung für True und 0 ist die
Abkürzung für False.
So, bei den ganzen Zahlen, wie gesagt, plus gleich mal minus, kennen
Sie alles.
Die Division ist logischerweise ganzzahlig.
Warum logischerweise?
Naja, Sie könnten ja ein Kommaergebnis nicht speichern in einem
Integer.
Ein Integer, wie gesagt, ist eine ganze Zahl.
Also haben Sie auch eine ganzzahlige Rechnung.
Und Modulo, wie gesagt, ist der Rest.
Also wenn man zum Beispiel sagt, 17 div 5 ist gleich 3, weil 3 mal
eben die 5 reingeht in die 17, bleibt aber noch ein Rest von 2 und das
ist das Ergebnis von dem Modulo-Operator.
Modulo, wissen Sie, oder haben das schon mal vielleicht gehört, das
ist auch so ein bisschen im Informatiker-Jargon drin.
Wenn man sagt, informatisch ist einfach Modulo LA, dann meint man,
damit ist Informatikschutz einfach abgesehen von, bis auf den Rest von
Linear Algebra.
Also das Modulo hat auch die Informatiker-Umgangssprache gebracht.
Über- und Unterlauf.
Das ist jetzt so das Unangenehme, wo das einfache Denkmodell, dass das
ja genauso ist wie bei den Zahlen, wie wir es aus der Mathematik
kennen, wo leider dieses Denkmodell Brüche bekommt.
Und zwar ist es so, das sind ja, wie ich eingangs sagte, alles
endliche Wertebereiche.
Wenn Sie jetzt eine Rechenoperation machen und damit eine Zahl
erreichen, die nicht mehr in dem Wertebereich darstellbar ist, dann
gibt es einen sogenannten Über- oder Unterlauf.
Und schauen wir uns das mal an.
Ich nehme hier vom Typ Integer diese Werte hier 2 hoch 9 und 2 hoch 9.
Das ist klar, dass das eine positive Zahl geben sollte, nämlich 4 mal
10 hoch 9.
Aber es hat eine negative Zahl.
Tatsächlich.
Warum?
Weil man im Prinzip einen Wert produziert, der so viele Bits hat, die
passen nicht mehr in diesen Wertebereich von Integer rein in diese 4
Bits, äh 4 Bytes.
Und dann wird halt im Prinzip der vordere Teil abgeschnitten, der ist
einfach nicht mehr speicherbar.
Das alles übrig bleibt, das ist halt irgendeine Zeichenkette, wird
interpretiert wieder als normale Integerzahl und das ist in dem Fall
eine, wo auch das Vorzeichen gesetzt war und deswegen halt hier ein
negativer Wert rauskommt.
Aber es ist klar, mathematisch stimmt es nicht.
Beim Programmieren macht es durchaus Sinn, wenn Sie Code hinschreiben,
der ganz harmlos aussieht, x plus y, dass Sie sich mal überlegen, ich
addiere was, möglicherweise bin ich jetzt außerhalb vom Wertebereich.
Weil es ist ja klar, wenn ich zwei Zahlen hab, die gerade so am Rande
des darstellbaren Bereichs liegen und wenn ich die addiere, ist die
Summe außerhalb vom darstellbaren Bereich.
Und wie gesagt, da muss man halt dran denken.
Auch analog dazu, wenn Sie von einer Zahl etwas abziehen, die schon
die maximal kleinste darstellbare Zahl war bei Int, und noch eins
abziehen, dann haben Sie auch wieder einen Überlauf.
Sie bräuchten mehr Bits, als Sie haben.
Ein Bit wird weggeschmissen, das höchstwertigste und das, was übrig
bleibt, ist dann die Zahl wieder.
Damit das nicht zu schlimm wird mit dieser Über- und Unterlauferei,
hat man mal in Java als Standard einfach festgelegt, das ist jetzt
nicht logisch, aber es ist nützlich, dass man sagt, immer wenn mit
Byte und Short gerechnet wird, also Datentypen, die nur ein oder zwei
Byte haben, wo das relativ schnell passieren kann, weil, das kann doch
nicht so viel Zeit ausdrücken, dann ist das Ergebnis erst mal intern
beim Java-Rechner erst mal ein Integer.
Wenn Sie das dann wieder einem Short oder einem Byte zuweisen, dann
gehen natürlich wieder Bits verloren.
Sie erinnern sich, Integer hat mehr Bytes als Byte und Short.
Aber intern rechnet Java erst mal mit Integer.
Das heißt, da haben Sie noch keinen Überlauf, wenn Sie das Ergebnis
dann auch in einem Integer speichern.
Wenn Sie das Ergebnis, wie gesagt, wieder mit aller Gewalt in einen
Short reinpressen, dann haben Sie natürlich doch wieder den Überlauf.
Wohingegen, wenn Sie mit Integer rechnen, da haben Sie hier schon den
Überlauf und müssten das vorher quasi in einen Long-Wert umwandeln,
damit Sie nicht den Überlauf haben.
So, dann gibt es noch weitere Operationen.
Plus, Plus und Minus, Minus.
Das sind eigentlich nur bequeme Abkürzungen.
Plus, Plus heißt, es wird ein Wert addiert.
Minus, Minus heißt, es wird die Eins abgezogen.
Also Plus, Plus, Eins dazu.
Minus, Minus, Eins weniger davon.
Und die Sache wird nur deswegen ein bisschen komplizierter, weil Sie
können das einmal als Präfix und einmal als Postfix anwenden auf eine
Variable.
Plus, Plus und X heißt, ich rechne erstmal 1 zum X dazu und das
Ergebnis dessen ist dann, wenn Sie das als Ausdruck auswerten, X plus
1.
Ja, das ist erstmal dazugerechnet.
Wenn Sie es als Postfix hinschreiben, X plus Plus und das Ganze als
Ausdruck auswerten, bekommen Sie den Wert von X und danach wird X um 1
erhöht.
Wenn Sie das einfach nur so hinschreiben, ohne Ausdruck weiter drum
herum, also einfach nur hinschreiben, X plus Plus, X minus oder Plus
Plus X, ist das Ergebnis dasselbe.
X ist danach um den Wert um 1 erhöht.
Aber wie gesagt, wenn Sie das Ganze nochmal in irgendeinen Ausdruck
reinpacken, dann macht es einen Unterschied, weil bei dem einen
bekommen Sie den Wert von X, in der Postfix-Schreibweise, in der
Präfix -Schreibweise, bekommen Sie den Wert von X plus 1.
Analog, dasselbe gilt für Minus, Minus mit 1 abziehen.
Hier sieht man das nochmal.
Y ist gleich Plus Plus X, dann bekommen Sie im Prinzip in Y den Wert X
plus 1 hin, weil erst wird das Erhöhen gemacht und dann die Zuweisung,
wohingegen hier Y ist gleich X plus Plus, bekommt Y den Wert von X und
danach wird X um 1 erhöht.
So, jetzt wird es ein bisschen spannender schon und zwar jetzt kommen
wir zum Thema Gleitkommazahlen.
Erstmal, wenn wir entweder eingesehen haben oder wir glauben, ich kann
jede Dezimalzahl auch in eine Binärzahl umwandeln, könnte man sagen,
gut, dann mache ich das mit den Gleitkommazahlen so, dass ich halt
quasi mir das so vorstelle wie zwei Integerzahlen.
Eine eben, die vor dem Komma steht und eine, die nach dem Komma steht
und das kann ich ja Kommazahlen darstellen.
Stimmt auch.
Das sind die sogenannten Festkommazahlen.
Der Nachteil bei diesen Festkommazahlen ist, dass sie halt unter
Umständen sowas darstellen wollen wie 1, Periode 1.
Das heißt, einen nicht allzu großen Wert, das heißt, das Byte oder den
Integer, den sie vor dem Komma haben, würde nicht ganz ausgenutzt.
Da wären sehr viele Bits null, weil sie ja auch potenziell sehr große
Zahlen darstellen wollen, aber die nutzen sie bei so einer Zahl nicht
aus und die Genauigkeit fehlt ihnen natürlich hintendran für den
Nachkommabereich.
Umgedreht, wenn sie eine Zahl darstellen wollten wie was weiß ich, 200
.000 Komma 0, dann sind alle Bits quasi vom Nachkommateil auch wieder
null und sie nutzen diese Bits nicht aus, um den Wert vor dem Komma
darzustellen.
Gut, bei 200.000 klappt das noch mit dem Integer, aber wenn sie einen
größeren Wert hätten, nicht.
Das heißt, man ist so ein bisschen unflexibel, wenn ich diese
Festkommazahlen nehme.
Ich muss sehr viele Bits reservieren, einmal für die Genauigkeit im
Nachkommabereich, auf der anderen Seite aber auch für die absolute
Größe der Zahl.
Und da ich nicht unbedingt immer beides gleichzeitig brauche für eine
Zahlenrepräsentation, hat man sich mal überlegt und hat gesagt, wir
machen jetzt eigentlich das Flexibel.
Das sind die sogenannten Gleitkommazahlen.
Gleitkommazahlen hat man ja, man kann sich das wirklich vorstellen,
ein gleitendes Komma, das ist nicht mehr in einer festen Stelle und im
Prinzip ist dieser IEEE Standard, den ich Ihnen jetzt zeige, dieser
IEEE 754 Standard, der Standard, der mal festgelegt wurde, wie codiert
man Gleitkommazahlen und das ist auch der gängige Standard, wie er
auch in den Programmiersprachen C, Java, C++ usw.
verwendet wird.
Es ist nicht die schlauste Art und Weise, es zu machen.
Es gibt Wissenschaftler, die intelligentere Methoden vorgeschlagen
haben, die auch viele der Nachteile nicht haben, die wir hier gleich
sehen werden.
Es ist aber auch nicht der allerdünnste Standard.
Und zwar beruht das Ganze auf der Idee, dass man sagt, ich kann
eigentlich jede Kommazahl immer umschreiben in jede Kommazahl x in
entweder eine negative Zahl oder eine positive Zahl.
Das wird ausgedrückt mit "-1 hoch Vorzeichen".
Wenn Vorzeichen 1 ist, habe ich hier "-1 hoch 1", also "-1".
Wenn das Vorzeichen mit 0 ist, habe ich "-1 hoch 0", was per
Definition 1 ist, also positive Zahl.
Dann eine Mantisse, das ist das große M, und dann 2 hoch einen
Exponenten.
Ich kann jede Zahl quasi umschreiben in dieses Format einer Mantisse
und eines Exponenten.
Schauen wir uns das mal am Beispiel an.
Wenn ich die Zahl "-2,5 habe, ist das einmal "-1 hoch 1", also das
Vorzeichen ist auf 1, also das Vorzeichen ist gesetzt, und dann 5 mal
2 hoch "-1".
Sie wissen, 2 hoch "-1", ist dasselbe wie 1 durch 2.
Das heißt, wir haben 5 mal 0,5, also 2,5 mal "-1", minus 2,5.
Wir haben aber jetzt eben die "-2,5", dargestellt in dieser
Schreibweise, eben "-1 hoch Vorzeichen", mal Mantisse, mal 2 hoch
Exponent.
Im Prinzip können Sie das natürlich auch mit einer anderen Basis als 2
machen, aber Sie ahnen, dass natürlich 2 binäre Codierung ist, was
natürlich nach einem Computer funktioniert.
Und deswegen hat man das ja hier mal so festgelegt.
Auf die Art und Weise, Sie kennen das vielleicht von Taschenrechnern,
macht man das nur mit der Zehnerpotenz, weil das für uns Menschen
geläufiger ist, dass man eben eine Zahl manchmal auch darstellt, 6,0 2
,2 e 23, das heißt dann, das kennen Sie vielleicht vom Taschenrechner,
wenn Sie das jemals hatten, wie gesagt, 6,0 2,2 mal 10 hoch 23.
Wie gesagt, die selbe Idee, nur binär ausgedrückt, ist letzten Endes
hier für das Zweiersystem.
So, soweit, so gut.
Es gibt jetzt besondere Werte dabei.
Wie die kodiert werden, wir sehen mal gleich.
Das eine ist NAN, not a number.
Und dann gibt es noch positiv und endlich und negativ und endlich.
Das sind quasi speziell kodierte Werte, mit denen kann man nicht
rechnen.
Das sind keine Zahlen, aber es sind Werte, die in diesem Integer-Float
oder auch Double vorgesehen sind.
Mathematische Operationen werden gerundet.
Das heißt, es wird über den nächsten Wert gesucht, der irgendwo
darstellbar ist.
Sie haben also auch für die höherwertigen mathematischen Operationen
Sinus, Cosinus, Quadratwurzel und so weiter, eine Klasse Math in Java
schon fest vorgegeben, die ist auch schon implementiert, die Ihnen
sowas bereitstellt.
Das müssen Sie im Prinzip nicht von Hand alles neu machen.
Es ist tatsächlich die Gleichheit, mit diesem Gleich, Gleich,
Ungleich, also Ausrufezeichen Gleich, auch definiert auf
Gleitkommazahlen, wie wir sie kannten von den Ganzzahlen, aber
Achtung, wegen der Rundungsfehler ist das in der Regel nicht sinnvoll
nutzbar.
Das heißt, im Default sollten Sie, auch wenn man das hinschreiben darf
in Java, nicht eben hinschreiben x gleich gleich y, sondern Sie
sollten im Prinzip den Betrag von der Differenz nehmen und einfach
sich selber Schwellwert festlegen für Ihre Anwendung, wo man sagt,
wenn der kleiner ist als 10 hoch minus 8, dann bin ich bereit zu
sagen, ja, die sind gleich, weil offenbar nur ein kleines Epsilon,
quasi eine kleine Differenz besteht.
Das macht man deswegen, weil eben immer gerundet wird und dabei
entstehen Rundungsfehler und das heißt, selbst wenn zwei Werte
eigentlich gleich sein sollten von der mathematischen Operation her,
aufgrund der Rundungsfehler, sind sie es vielleicht nicht, aber
trotzdem sollte im Programmfluss so getan werden, als ob sie gleich
sein.
Und deswegen, wie gesagt, schaut man sich eben die Differenz und davon
den Absolutbetrag an und sagt halt, das muss kleiner sein als ein
Epsilon, was ich vorgebe.
Und wie groß das jetzt ist, ob das immer 10 hoch minus 8 sein muss,
das hängt natürlich von Ihrer Anwendung ab, wie fein Sie quasi
auflösen möchten für Gleichheit.
Und das analog gilt es natürlich dann auch für das Ungleich
entsprechend.
So, jetzt kommen wir zu einer Einstein-Folie, die aber im Wesentlichen
das gleiche nochmal sagt, wie eben.
Wenn ich 32 Bit Zahlen habe, habe ich 1 Bit Vorzeichen, 8 Bit Exponent
und die restlichen Bits, 23 Bits, sind in der Mantisse.
In der Double Precision mit 64 Bits ist es mal so festgelegt worden,
dass man sagt, 1 Bit Vorzeichen wieder, 11 Bit Exponent, 32 Bit
Mantisse.
Und das Ganze rechnet sich, wie ich das eben auch gesagt habe,
Vorzeichen, Bit, mal Mantisse, mal Basis, hoch Exponent und die Basis
ist halt immer 2, naheliegenderweise, weil ich es ja binär darstellen
will.
Und jetzt ist es so, man hat eine Konvention festgelegt, in der man
sagt, zum Beispiel jetzt, ich könnte ja die Zahl 23 darstellen als 23
mal 10 hoch 1, ich könnte sie aber auch als 2,3 mal 10 darstellen.
Das heißt also, jetzt mal Basis 10, für uns leichter zu rechnen.
Das heißt, ich habe für dieselbe Zahl, für denselben Wert,
verschiedene Darstellungen.
Das ist natürlich keine gute Idee.
Warum ist das keine gute Idee?
Aus zwei Gründen.
Erstens mal, verschenke ich ja offenbar darstellbare Zahlen.
Wenn ich zweimal dieselbe Zahl auf unterschiedliche Weise darstelle,
habe ich schon zwei Darstellungen für dieselbe Zahl verbraten.
Schlecht.
Fehlt mir woanders an Auflösungen oder an größeren Zahlen, die ich
darstelle.
Zum anderen ist es auch so, wenn ich Zahlen vergleichen will, aber die
sind unterschiedlich kodiert, ich vergleich die Kodierungen, weiß ich
nicht, sind die Zahlen wirklich unterschiedlich oder ist es vielleicht
doch dieselbe Zahl?
Auch nicht gut.
Und deswegen hat man sich überlegt, dass man sagt, ich normalisiere
diese Gleitkommadarstellung.
Das heißt, ich mache den Exponenten so hoch, dass vor dem Komma immer
eine 1 steht.
Warum keine 2?
Naja, dann könnten Sie ja nochmal durch 2 teilen.