Rechnen mit Brüchen - objektorientierte Programmierung

Prokyon114

Aktives Mitglied
Hallo,

als neues Mitglied hier im Forum möchte ich erstmal alle recht herzlich begrüßen!

Jedenfalls sitze ich seit mehreren Stunden schon an einer Aufgabe, in der es darum geht, ein Programm zu schreiben, das einige Dinge mit Brüchen anstellt. Ich habe alles so gut es ging geschrieben, jedoch scheint einiges schief gelaufen zu sein. Erstmal meckert der Compiler wegen des Overrides rum, und wenn dieses nicht da ist, gibt das Programm leider nur fehlerhafte Werte aus.

Ich würde mich freuen, wenn ihr mal drüberschauen könntet und mir Hinweise gebt, wie ich das Programm verbessern kann.

Java:
/**
* Eine Klasse zur Repraesentierung von Bruchzahlen.
*/
public class Fraction {

    /* Je ein streng gekapseltes Attribut fuer den ganzzahligen Zaehler und Nenner. */

    private int numerator;
    private int denominator;

    /**
     * Vollstaendig parametrisierter Konstruktor der Zaehler und Nenner
     * uebergeben bekommt und die entsprechenden Attribute setzt.
     * Negative Vorzeichen (Zahlen kleiner als Null) duerfen nur im Zaehler
     * auftreten, die Uebergabe eines negativen Nenners ist zulaessig und
     * wird mathematisch korrekt behandelt.
     * Wird eine Null als Nenner uebergeben, so wird das entsprechende Attribut
     * auf Eins gesetzt.
     */
    public Fraction(int pNumerator, int pDenominator) {
        if(pDenominator<0){
            numerator = -pNumerator;
            denominator = -pDenominator;
        }
        if(pDenominator == 0){
            denominator = 1;
        }
    }

    /**
     * Gibt den Nenner zurueck.
     */
    public int getDenominator() {
        return denominator;
    }

    /**
     * Gibt den Zaehler zurueck.
     */
    public int getNumerator() {
        return numerator;
    }

    /**
     * Gibt den Bruch als Gleitkommazahl zurueck.
     */
    @Override
    public double toDouble(int numerator, int denominator) {
        double fraction = numerator / denominator;
        return fraction;
    }

    /**
     * Ueberschreibt die toString-Methode und gibt einen String im Format
     * "Zaehler/Nenner = Kommazahl" zurueck.
     */
    public String toString(double fraction) {
        String kommazahl = Double.toString(fraction);
        return numerator + "/" + denominator + " = " + kommazahl;
    }

    /**
     * Gibt den groessten gemeinsamen Teiler (Greatest Common Divider) zurueck.
     */
    public static int GCD(int x, int y) {
        if (y == 0) {
            return x;
        }
        return GCD(y, x % y);
    }

    /**
     * Gibt das kleinste gemeinsame Vielfache (Lowest Common Multiple) zurueck.
     */
    public static int LCM(int x, int y) {
        return (x * y) / GCD(x, y);
    }

    /**
     * Kuerzt (vereinfacht) den Bruch.
     */
    public void cancel() {
        int factorC = GCD(numerator, denominator);
        numerator = numerator/factorC;
        denominator = denominator/factorC;
    }

    /**
     * Erweitert, macht gleichnamig, addiert den uebergebenen Bruch und kuerzt.
     */
    public void add(Fraction f) {
        numerator = numerator * f.denominator + f.numerator * denominator;
        denominator = denominator * f.denominator;
        cancel();
    }

    /**
     * Multipliziert den uebergebenen Bruch und kuerzt.
     */
    public void multiply(Fraction f) {
        numerator = numerator * f.numerator;
        denominator = denominator * f.denominator;
        cancel();
    }

    /**
     * Bildet den Kehrwert und kuerzt, wenn der Zaehler ungleich Null ist.
     * Stellt dabei sicher, dass negative Vorzeichen nur im Zaehler auftreten.
     */
    public void reciprocal() {
        if(denominator<0){
            numerator = -numerator;
            denominator = -denominator;
        }
        if(denominator != 0){
            int newNumerator = numerator;
            int newDenominator = denominator;
            numerator = newDenominator;
            denominator = newNumerator;
            cancel();
        }
    }
   
    /**
     * Dividiert den uebergebenen Bruch
     * (unter Verwendung von Kehrwert und Multiplikation).
     */
    public void divide(Fraction f) {
        numerator = numerator * f.denominator;
        denominator = denominator * f.numerator;
        cancel();
    }

    /**
     * Deklariert und initialisiert zwei Ganzzahlen:
     * "numerator" und "denominator" mit den Werten 1 und -7.
     * Deklariert und initialisiert zwei Gleitkommazahlen:
     * "floatNumber" und "floatResult" mit dem Quotienten aus
     * "numerator" und "denominator" (dem Ergebnis der Gleitkommarechnung).
     * Erzeugt zwei Objekte vom Typ "Fraction":
     * "fractionNumber" und "fractionResult" mit "numerator" als Zaehler und
     * "denominator" als Nenner.
     * Addiert "Betrag von denominator -1" Mal "floatNumber" zu "floatResult"
     * und genauso oft "fractionNumber" zu "fractionResult".
     * Gibt mit verstaendlicher Beschreibung "floatResult" und "fractionResult"
     * aus.
     * Bildet den Kehrwert von "fractionNumber" und fuehrt dann die Division
     * "fractionResult/fractionNumber" aus.
     * Gibt mit verstaendlicher Beschreibung "fractionNumber"
     * und "fractionResult" aus.
     */
    public static void main(String[] args) {
        int numerator = 1;
        int denominator = -7;

        float floatNumber = numerator / denominator;
        float floatResult = numerator / denominator;

        Fraction fractionNumber = new Fraction(numerator, denominator);
        Fraction fractionResult = new Fraction(numerator, denominator);

        floatResult += floatNumber * (denominator-1);
        fractionResult.numerator = (denominator-1)*fractionNumber.numerator;
        fractionResult = new Fraction(fractionResult.numerator, denominator);

        System.out.println("Der Bruch als Kommazahl ist: "+floatResult);
        System.out.println("Der Bruch ist: "+fractionResult.toString());

        fractionNumber.reciprocal();
        fractionResult.divide(fractionNumber);

        System.out.println("Der Bruch als Kommazahl ist: "+fractionNumber.toString());
        System.out.println("Der Bruch ist: "+fractionResult.toString());


    /**
     * To be filled
     */  
   
    }
}
 
Das Override ist dort falsch, weil du dort keine Methode überschreibst. Override darf nur an (und sollte an allen) Methoden stehen, die eine Methode der Oberklasse überschreiben.


Dann sollte toString wahrscheinlich keinen Parameter haben (der Parameter ergibt sich ja auch aus dem Bruch selbst). In deiner Ausgabe rufst du immer die normale, von Objekt geerbte, toString auf - diese solltest du überschreiben.


Welche Dinge sind denn noch falsch?
 
Stimmt das Override sollte eigentlich an anderer Stelle stehen (s.u.). Ich bekomme dann immer das Problem, dass der Compiler keine Superklasse findet ("error: method does not override or implement a method from a supertype @Override"). Ich weiß aber nicht, wie ich das beheben kann.

Code:
 /**
     * Gibt den Bruch als Gleitkommazahl zurueck.
     */
    public double toDouble(int numerator, int denominator) {
        double fraction = numerator / denominator;
        return fraction;
    }

    /**
     * Ueberschreibt die toString-Methode und gibt einen String im Format
     * "Zaehler/Nenner = Kommazahl" zurueck.
     */
    @Override
    public String toString(double fraction) {
        String kommazahl = Double.toString(fraction);
        return numerator + "/" + denominator + " = " + kommazahl;
    }
 
Stimmt das Override sollte eigentlich an anderer Stelle stehen (s.u.). Ich bekomme dann immer das Problem, dass der Compiler keine Superklasse findet ("error: method does not override or implement a method from a supertype @Override"). Ich weiß aber nicht, wie ich das beheben kann.
Die Antwort hat dir @mrBrown schon gegeben, ehe Du gefragt hast:

Dann sollte toString wahrscheinlich keinen Parameter haben (der Parameter ergibt sich ja auch aus dem Bruch selbst). In deiner Ausgabe rufst du immer die normale, von Objekt geerbte, toString auf - diese solltest du überschreiben.
 
Ja, habe es jetzt erst begriffen, was damit gemeint war🙄 Aber vielen Dank! In der Uni habe ich leider das Gefühl, das man von einem Thema zum anderem sprintet und dadurch teilweise große Lücken offen bleiben. Aber danke für eure Unterstützung!
 
Jetzt stellt sich mir die Frage, wie ich die Variable 'double fraction' in die toString Methode reinbekomme, es kommt immer "cannot find symbol"

Java:
/**
     * Gibt den Bruch als Gleitkommazahl zurueck.
     */
    public double toDouble(int numerator, int denominator) {
        double fraction = numerator / denominator;
        return fraction;
    }

    /**
     * Ueberschreibt die toString-Methode und gibt einen String im Format
     * "Zaehler/Nenner = Kommazahl" zurueck.
     */
    @Override
    public String toString(){
        return numerator + "/" + denominator + " = " + fraction;
    }
 
Nimm ein paar Vorlesungen raus und schieb die in die nächste Runde. Das versaut dir zwar die Regelstudienzeit, aber die ist auch nicht wichtig (außer du beziehst Bafög). Du wirst nicht gut, indem du gute Klausuren schreibst, sondern indem du deine Interessen und Neugier auslebst.

So, wie das Studium heute aufgebaut ist, kann man allenfalls etwas lernen. Aber eigentlich sollte man während des Studiums nicht erlernen, sondern erkennen, begreifen, den Stoff fachlich durchdringen.

Zum Thema:
Schau dir mal 'Double' (nicht mit double verwechseln) an.
 
Nimm ein paar Vorlesungen raus und schieb die in die nächste Runde. Das versaut dir zwar die Regelstudienzeit, aber die ist auch nicht wichtig (außer du beziehst Bafög). Du wirst nicht gut, indem du gute Klausuren schreibst, sondern indem du deine Interessen und Neugier auslebst.

Habe ich bereits, obwohl ich Bafög beziehe, aber durch mein zeitintensives Hobby funktioniert es trotzdem nicht so wie ich es mir wünsche, war aber auch privat einiges los in den letzten Wochen...

Jedenfalls habe ich nun folgendes:

Java:
/**
 * Eine Klasse zur Repraesentierung von Bruchzahlen.
 */
public class Fraction {

    /* Je ein streng gekapseltes Attribut fuer den ganzzahligen Zaehler und Nenner. */

    private int numerator;
    private int denominator;

    /**
     * Vollstaendig parametrisierter Konstruktor der Zaehler und Nenner
     * uebergeben bekommt und die entsprechenden Attribute setzt.
     * Negative Vorzeichen (Zahlen kleiner als Null) duerfen nur im Zaehler
     * auftreten, die Uebergabe eines negativen Nenners ist zulaessig und
     * wird mathematisch korrekt behandelt.
     * Wird eine Null als Nenner uebergeben, so wird das entsprechende Attribut
     * auf Eins gesetzt.
     */
    public Fraction(int pNumerator, int pDenominator) {
        if(pDenominator<0){
            numerator = -pNumerator;
            denominator = -pDenominator;
        }
        if(pDenominator == 0){
            denominator = 1;
        }
    }

    /**
     * Gibt den Nenner zurueck.
     */
    public int getDenominator() {
        return denominator;
    }

    /**
     * Gibt den Zaehler zurueck.
     */
    public int getNumerator() {
        return numerator;
    }

    /**
     * Gibt den Bruch als Gleitkommazahl zurueck.
     */
    public double toDouble() {
        double fraction = numerator / denominator;
        return fraction;
    }

    /**
     * Ueberschreibt die toString-Methode und gibt einen String im Format
     * "Zaehler/Nenner = Kommazahl" zurueck.
     */
    @Override
    public String toString(){
        double fraction = toDouble();
        return numerator + "/" + denominator + " = " + fraction;
    }

    /**
     * Gibt den groessten gemeinsamen Teiler (Greatest Common Divider) zurueck.
     */
    public static int GCD(int x, int y) {
        if (y == 0) {
            return x;
        }
        return GCD(y, x % y);
    }

    /**
     * Gibt das kleinste gemeinsame Vielfache (Lowest Common Multiple) zurueck.
     */
    public static int LCM(int x, int y) {
        return (x * y) / GCD(x, y);
    }

    /**
     * Kuerzt (vereinfacht) den Bruch.
     */
    public void cancel() {
        int factorC = GCD(numerator, denominator);
        numerator = numerator/factorC;
        denominator = denominator/factorC;
    }

    /**
     * Erweitert, macht gleichnamig, addiert den uebergebenen Bruch und kuerzt.
     */
    public void add(Fraction f) {
        numerator = numerator * f.denominator + f.numerator * denominator;
        denominator = denominator * f.denominator;
        cancel();
    }

    /**
     * Multipliziert den uebergebenen Bruch und kuerzt.
     */
    public void multiply(Fraction f) {
        numerator = numerator * f.numerator;
        denominator = denominator * f.denominator;
        cancel();
    }

    /**
     * Bildet den Kehrwert und kuerzt, wenn der Zaehler ungleich Null ist.
     * Stellt dabei sicher, dass negative Vorzeichen nur im Zaehler auftreten.
     */
    public void reciprocal() {
        if(denominator<0){
            numerator = -numerator;
            denominator = -denominator;
        }
        if(denominator != 0){
            int newNumerator = numerator;
            int newDenominator = denominator;
            numerator = newDenominator;
            denominator = newNumerator;
            cancel();
        }
    }
    
    /**
     * Dividiert den uebergebenen Bruch
     * (unter Verwendung von Kehrwert und Multiplikation).
     */
    public void divide(Fraction f) {
        numerator = numerator * f.denominator;
        denominator = denominator * f.numerator;
        cancel();
    }

    /**
     * Deklariert und initialisiert zwei Ganzzahlen:
     * "numerator" und "denominator" mit den Werten 1 und -7.
     * Deklariert und initialisiert zwei Gleitkommazahlen:
     * "floatNumber" und "floatResult" mit dem Quotienten aus
     * "numerator" und "denominator" (dem Ergebnis der Gleitkommarechnung).
     * Erzeugt zwei Objekte vom Typ "Fraction":
     * "fractionNumber" und "fractionResult" mit "numerator" als Zaehler und
     * "denominator" als Nenner.
     * Addiert "Betrag von denominator -1" Mal "floatNumber" zu "floatResult"
     * und genauso oft "fractionNumber" zu "fractionResult".
     * Gibt mit verstaendlicher Beschreibung "floatResult" und "fractionResult"
     * aus.
     * Bildet den Kehrwert von "fractionNumber" und fuehrt dann die Division
     * "fractionResult/fractionNumber" aus.
     * Gibt mit verstaendlicher Beschreibung "fractionNumber"
     * und "fractionResult" aus.
     */
    public static void main(String[] args) {
        int numerator = 1;
        int denominator = -7;

        float floatNumber = numerator / denominator;
        float floatResult = numerator / denominator;

        Fraction fractionNumber = new Fraction(numerator, denominator);
        Fraction fractionResult = new Fraction(numerator, denominator);

        floatResult += floatNumber * (denominator-1);
        fractionResult.numerator = (denominator-1)*fractionNumber.numerator;
        fractionResult = new Fraction(fractionResult.numerator, denominator);

        System.out.println("Der Bruch als Kommazahl ist: "+floatResult);
        System.out.println("Der Bruch ist: "+fractionResult.toString());

        fractionNumber.reciprocal();
        fractionResult.divide(fractionNumber);

        System.out.println("Der Bruch als Kommazahl ist: "+fractionNumber.toString());
        System.out.println("Der Bruch ist: "+fractionResult.toString());


    /**
     * To be filled
     */   
    
    }
}

Das Programm lässt sich kompilieren (Juhu!), aber es kommen immer noch falsche Werte auf der Konsole raus:

Der Bruch als Kommazahl ist: 0.0
Der Bruch ist: -8/7 = -1.0
Der Bruch als Kommazahl ist: -7/1 = -7.0
Der Bruch ist: 8/49 = 0.0

Mir scheint, ich übersehe noch einiges bzw. meine "Betragsberechnung" läuft noch nicht so wirklich...
 
Das scheint mir eine gute Gelegenheit, den Debugger kennenzulernen. Geh das Programm mal Schritt für Schritt durch und schau dir dabei die Variablenwerte an.
 
Durch die Ausgabe, kann man den Debugger fast sparen, denn er zeigt das Problem ja schon an:
-8 / 7 = -1.0

Wie kann es zu so einem Ergebnis kommen? Der Code, der diese Berechnung macht, ist ja:
Java:
double fraction = numerator / denominator;

Dazu evtl. den Code in zwei Teile aufbrechen: Was kommt bei
Code:
numerator / denominator
raus?
 
Naja, es scheint schon viel früher etwas nicht zu stimmen, ich habe mal Testweise eine Ausgabe hinzugefügt:

Java:
public static void main(String[] args) {
        int numerator = 1;
        int denominator = -7;

        float floatNumber = numerator / denominator;
        float floatResult = numerator / denominator;

        System.out.println("floatNumber: "+floatNumber);
        System.out.println("floatResult: "+floatResult);

Auf der Konsole wird dann folgendes ausgegeben:

floatNumber: 0.0
floatResult: 0.0

Ich habe die Vermutung, dass der Konstruktor nicht richtig funktioniert, komme jedoch auf keinen grünen Zweig
 
Okay, ich denke, es sollte jetzt alles stimmen:

Java:
/**
 * Eine Klasse zur Repraesentierung von Bruchzahlen.
 */
public class Fraction {

    /* Je ein streng gekapseltes Attribut fuer den ganzzahligen Zaehler und Nenner. */

    private int numerator;
    private int denominator;

    /**
     * Vollstaendig parametrisierter Konstruktor der Zaehler und Nenner
     * uebergeben bekommt und die entsprechenden Attribute setzt.
     * Negative Vorzeichen (Zahlen kleiner als Null) duerfen nur im Zaehler
     * auftreten, die Uebergabe eines negativen Nenners ist zulaessig und
     * wird mathematisch korrekt behandelt.
     * Wird eine Null als Nenner uebergeben, so wird das entsprechende Attribut
     * auf Eins gesetzt.
     */
    public Fraction(int pNumerator, int pDenominator) {

        if(pDenominator<0){
            numerator = -pNumerator;
            denominator = -pDenominator;
        }
        if(pDenominator == 0){
            denominator = 1;
        }
    }

    /**
     * Gibt den Nenner zurueck.
     */
    public int getDenominator() {
        return denominator;
    }

    /**
     * Gibt den Zaehler zurueck.
     */
    public int getNumerator() {
        return numerator;
    }

    /**
     * Gibt den Bruch als Gleitkommazahl zurueck.
     */
    public double toDouble() {
        double fraction = (double)numerator / denominator;
        return fraction;
    }

    /**
     * Ueberschreibt die toString-Methode und gibt einen String im Format
     * "Zaehler/Nenner = Kommazahl" zurueck.
     */
    @Override
    public String toString(){
        double fraction = toDouble();
        return numerator + "/" + denominator + " = " + fraction;
    }

    /**
     * Gibt den groessten gemeinsamen Teiler (Greatest Common Divider) zurueck.
     */
    public static int GCD(int x, int y) {
        if (y == 0) {
            return x;
        }
        return GCD(y, x % y);
    }

    /**
     * Gibt das kleinste gemeinsame Vielfache (Lowest Common Multiple) zurueck.
     */
    public static int LCM(int x, int y) {
        return (x * y) / GCD(x, y);
    }

    /**
     * Kuerzt (vereinfacht) den Bruch.
     */
    public void cancel() {
        int factorC = GCD(numerator, denominator);
        numerator = numerator/factorC;
        denominator = denominator/factorC;
    }

    /**
     * Erweitert, macht gleichnamig, addiert den uebergebenen Bruch und kuerzt.
     */
    public void add(Fraction f) {
        numerator = numerator * f.denominator + f.numerator * denominator;
        denominator = denominator * f.denominator;
        cancel();
    }

    /**
     * Multipliziert den uebergebenen Bruch und kuerzt.
     */
    public void multiply(Fraction f) {
        numerator = numerator * f.numerator;
        denominator = denominator * f.denominator;
        cancel();
    }

    /**
     * Bildet den Kehrwert und kuerzt, wenn der Zaehler ungleich Null ist.
     * Stellt dabei sicher, dass negative Vorzeichen nur im Zaehler auftreten.
     */
    public void reciprocal() {
        if(denominator<0){
            numerator = -numerator;
            denominator = -denominator;
        }
        if(denominator != 0){
            int newNumerator = numerator;
            int newDenominator = denominator;
            numerator = newDenominator;
            denominator = newNumerator;
            cancel();
        }
    }
    
    /**
     * Dividiert den uebergebenen Bruch
     * (unter Verwendung von Kehrwert und Multiplikation).
     */
    public void divide(Fraction f) {
        numerator = numerator * f.denominator;
        denominator = denominator * f.numerator;
        cancel();
    }

    /**
     * Deklariert und initialisiert zwei Ganzzahlen:
     * "numerator" und "denominator" mit den Werten 1 und -7.
     * Deklariert und initialisiert zwei Gleitkommazahlen:
     * "floatNumber" und "floatResult" mit dem Quotienten aus
     * "numerator" und "denominator" (dem Ergebnis der Gleitkommarechnung).
     * Erzeugt zwei Objekte vom Typ "Fraction":
     * "fractionNumber" und "fractionResult" mit "numerator" als Zaehler und
     * "denominator" als Nenner.
     * Addiert "Betrag von denominator -1" Mal "floatNumber" zu "floatResult"
     * und genauso oft "fractionNumber" zu "fractionResult".
     * Gibt mit verstaendlicher Beschreibung "floatResult" und "fractionResult"
     * aus.
     * Bildet den Kehrwert von "fractionNumber" und fuehrt dann die Division
     * "fractionResult/fractionNumber" aus.
     * Gibt mit verstaendlicher Beschreibung "fractionNumber"
     * und "fractionResult" aus.
     */
    public static void main(String[] args) {
        int numerator = 1;
        int denominator = -7;

        float floatNumber = (float)numerator / denominator;
        float floatResult = (float)numerator / denominator;

        Fraction fractionNumber = new Fraction(numerator, denominator);
        Fraction fractionResult = new Fraction(numerator, denominator);

        floatResult += floatNumber * (Math.abs(denominator)-1);
        fractionResult.numerator = (Math.abs(denominator)-1)*fractionNumber.numerator;
        fractionResult = new Fraction(fractionResult.numerator, denominator);

        System.out.println("6 mal der Bruch als Kommazahl addiert zum Bruch als Kommazahl ergibt: "+floatResult);
        System.out.println("6 mal der Bruch addiert zum Bruch ergibt: "+fractionResult.toString());

        fractionNumber.reciprocal();
        fractionResult.divide(fractionNumber);

        System.out.println("Der Kehrwert des Bruchs: "+fractionNumber.toString());
        System.out.println("Der Bruch geteilt durch den obigen Kehrwert ist: "+fractionResult.toString());


    /**
     * To be filled
     */   
    
    }
}


Konsole gibt folgendes aus:

Code:
6 mal der Bruch als Kommazahl addiert zum Bruch als Kommazahl ergibt: -1.0000001
6 mal der Bruch addiert zum Bruch ergibt: 6/7 = 0.8571428571428571
Der Kehrwert des Bruchs: -7/1 = -7.0
Der Bruch geteilt durch den obigen Kehrwert ist: -6/49 = -0.12244897959183673
 

Zurück
Oben