Java Videotraining Werbung

Videotraining Spring 3 Boot Werbung

1. Mathematisches

Schon in den ersten Aufgaben kamen Ganzzahlen und Fließkommazahlen vor; wir haben die Zahlen mit den üblichen mathematischen Operatoren verrechnet. Nicht für alles hat Java einen Operator, und so bietet die Klasse Math weitere Methoden und Konstanten; schon früh haben wir zum Beispiel Math.random() eingesetzt. In den folgenden Aufgaben dieses Kapitels geht es insbesondere um verschiedene Rundungen, und wir werden auf Methoden der Klasse Math schauen. Zudem bietet das Paket java.math zwei Klassen, mit denen sich beliebig große Zahlen repräsentieren lassen — auch dazu gibt es Aufgaben.

Voraussetzungen

  • Rundungsmethoden der Klasse Math kennen

  • BigInteger und BigDecimal kennen

Verwendete Datentypen in diesem Kapitel:

1.1. Die Klasse Math

Die Klasse Math enthält eine große Anzahl mathematischer Funktionen, aber es ist wichtig, auch zum Beispiel bei Wrapper-Klassen zu schauen oder beim Scanner oder Formatter, wenn es darum geht, eine Zahl in einen String zu konvertieren oder einen String zu parsen, sodass am Ende wieder ein primitiver Typ steht.

1.1.1. Prüfen, ob Tin Tin beim Runden betrogen hat ⭐

Die Buchhalterin Tin Tin macht für Captain CiaoCiao die Aufstellungen der Ein- und Ausgaben. Sie bekommt positive und negative Fließkommawerte und schreibt für eine Bilanz zum Schluss die Summe als gerundete Ganzzahl auf. Captain CiaoCiao hat den Verdacht, dass Tin Tin nicht ganz ehrlich ist und sich die Centbeträge unter den Nagel reißt, wobei sie die Summen doch korrekt kaufmännisch runden sollte. Zur Erinnerung: Ist die Zahl an der ersten wegfallenden Dezimalstelle eine 0, 1, 2, 3 oder 4, wird abgerundet; ist sie 5, 6, 7, 8 oder 9, wird aufgerundet.

Um seine Vermutung zu prüfen, benötigt Captain CiaoCiao ein Programm, das summieren und verschiedene Rundungsmodi prüfen kann.

Aufgabe:

  • Gegeben sind ein Array mit Fließkommazahlen (positiv und negativ) und die von Tin Tin in eine Ganzzahl konvertierte Summe.

  • Captain CiaoCiao möchte herausfinden, mit welcher Rundung die Ganzzahl der Summe gebildet wurde. Daher sollen die Elemente in dem Array summiert werden und mit der Summe von Tin Tin verglichen werden. Die Rundung wird erst nach der Addition der Zahlen durchgeführt.

  • Implementiere eine Methode RoundingMode detectRoundingMode(double[] numbers, int sum), die ein double-Array mit Zahlen und die Summe von Tin Tin bekommt und prüft, welcher Rundungsmodus verwendet wurde.

    • Damit sich der Rundungsmodus repräsentieren lässt, führe einen Aufzählungstyp ein:

      enum RoundingMode {
        CAST, ROUND, FLOOR, CEIL, RINT, UNKNOWN;
      }
    • Die Aufzählungselemente repräsentieren unterschiedliche Rundungsarten:

    • (int), also eine Typumwandlung

    • (int) Math.floor(double)

    • (int) Math.ceil(double)

    • (int) Math.rint(double)

    • (int) Math.round(double)

  • Welche Rundung ist für Captain CiaoCiao schlecht und für Tin Tin gut? Mit welcher Variante könnte Tin Tin betrügen?

Beispiel:

  • Der Aufruf könnte so aussehen:

    double[] numbers = { 199.99 };
    System.out.println( detectRoundingMode( numbers, 200 ) );

Hinweise:

  • Es gibt im Paket java.math den Aufzählungstyp RoundingMode, doch für unseren Fall passt er nicht auf die Aufgabe.

  • Es kann gut passieren, dass mehrere Rundungsmodi passen — etwa wenn die Summe der Fließkommawerte selbst eine Ganzzahl ergibt — dann kann die Methode sich für einen der Rundungsmodi frei entscheiden.

1.2. Große und sehr präzise Zahlen

Mit den Klassen java.math.BigInteger und java.math.BigDecimal lassen sich beliebig große Ganz- und Fließkommazahlen repräsentieren.

1.2.1. Arithmetischen Mittelwert einer großen Ganzzahl berechnen ⭐

Um den arithmetischen Mittelwert von zwei Zahlen zu berechnen, werden sie addiert und durch zwei geteilt. Das funktioniert dann gut, wenn die Summe der beiden Werte nicht über der größten darstellbaren Zahl liegt. Wenn es einen Überlauf gibt, ist das Ergebnis falsch. Es gibt in der Informatik einige Algorithmen, die auch mit diesem Problem umgehen können, doch wir können es uns etwas einfacher machen und den jeweils höheren Datentyp nehmen. Soll z. B. von zwei int-Variablen der Mittelwert berechnet werden, konvertieren wir die beiden int in ein long, addieren die Zahlen, dividieren und konvertieren zurück zum int. Beim Datentyp long gibt es keinen größeren primitiven Datentyp, doch lässt sich für den Fall gut der Typ BigInteger einsetzen.

Aufgabe:

  • Berechne den arithmetischen Mittelwert zweier long-Werte, sodass es nicht zu einem Überlauf und falschen Ergebnissen kommt. Das Ergebnis soll wieder ein long sein.

1.2.2. Zahl für Zahl über das Telefon ⭐

Durch einen neuen Deal hat Bonny Brain viel Geld verdient. Die Zahl ist so groß, dass man sie am Telefon gar nicht einfach durchsagen kann; sie muss in Häppchen übermittelt werden.

Number by number over the phone

Aufgabe:

  • Schreibe eine neue Methode BigInteger completeNumber(int... parts), die eine variable Anzahl von Zahlen bekommt und zum Schluss die große Gesamtzahl zurückgibt.

Beispiel:

  • completeNumber(123, 22, 989, 77, 9) liefert ein BigInteger mit dem Wert 12322989779.

1.2.3. Klasse für Brüche entwickeln und Brüche kürzen ⭐⭐

Bonny Brain probiert ein neues Rezept für einen Rumpunsch. In der Anweisung zur Zubereitung kommen immer wieder Brüche vor wie »1/4 Liter Traubensaft« oder »1/2 Liter Rum«. Für die Feier bereitet sie 100 Portionen vor, und es entstehen Brüche wie »100/4 Liter«. Die Brüche können gekürzt werden, sodass Bonny Brain zum Beispiel weiß, dass 25 Liter Traubensaft gekauft werden müssen.

Aufgabe:

  1. Lege eine neue Klasse Fraction an.

  2. Es soll einen Konstruktor Fraction(int numerator, int denominator) geben, der Zähler und Nenner in public final Variablen ablegt.

    • Überlege, ob es fehlerhafte Parameterbelegungen geben kann, die wir durch Ausnahmen melden sollten.

    • Jeder angelegte Bruch soll automatisch gekürzt werden. Greife dafür auf die Methode gcd(…​) von BigInteger zurück, die den größten gemeinsamen Teiler (ggT) (engl. greatest common divisor) berechnet. Zur Erinnerung: Der ggT von Zähler und Nenner ist die größte Zahl, durch die beide teilbar sind. Man kann den Bruch vollständig kürzen, indem man sowohl Zähler als auch Nenner durch diese Zahl teilt.

    • Zähler und Nenner können negativ sein, aber dann kann man das Vorzeichen umdrehen, sodass beide Werte positiv werden.

    • Die Objekte sollen alle immutable sein, und daher können die Variablen public sein, da sie nach dem Initialisieren über den Konstruktor nicht mehr verändert werden sollen. Anders gesagt: Die Klasse Fraction enthält keine Setter.

  3. Ergänze den Konstruktor Fraction(int value), bei dem der Nenner automatisch zu 1 wird.

  4. Implementiere eine Methode, mit denen man Brüche multiplizieren kann und die Überläufe erkennt.

  5. Implementiere eine Methode reciprocal(), die den Kehrbruch eines Bruches liefert, also Zähler und Nenner miteinander vertauscht. Mithilfe dieser Methode lässt sich die Division von Brüchen umsetzen.

  6. Fraction soll java.lang.Number erweitern und alle vorgeschriebenen Methoden implementieren.

  7. Fraction soll Comparable implementieren, denn Brüche lassen sich in eine Dezimalzahl umrechnen, und Dezimalzahlen haben eine natürliche Ordnung.

  8. Fraction soll equals(…​) und hashCode() korrekt implementieren.

  9. Implementiere eine toString()-Methode, die eine möglichst schlanke Rückgabe liefert.

Fraction UML
Abbildung 1. UML-Diagramm von Fraction