Klasse für Bruchzahlen gesucht



  • lol volkard hatte ziemlich viel geschrieben. alles falsch? 😮 😮 😮



  • @Shlo
    ich würde entweder implizite Casts von int zu fraction zulassen, was aber einige Risiken mit sich bringen kann oder noch Operatoren, die int als zweiten Parameter erwarten schreiben, damit man

    ab+cabc\frac{a}{b}+c \frac{a}{b}*c \ldots

    errechnen kann.

    Und num würde ich nicht unbedingt als default mit 1 belegen.



  • Volkard's Meinung würde mich auch interessieren.

    Aus der Klasse von Shlo, dem o.g. Link (Daniel Pronych) und Volkard's Klassenansatz kann man sich sicherlich eine gut ausgestattete Klasse Fraction deluxe zusammen basteln. Es ist aber schon erstaunlich, dass man so etwas nicht "von der Stange" downloaden kann.

    Ich poste hier zum Vergleich die nicht sonderlich üppig ausgestattete Klasse von Daniel Pronych:

    //Fraction.h
    #ifndef BIGGEST_INT
    #define BIGGEST_INT int
    #endif
    
    #ifndef _FRACTION_
    #define _FRACTION_
    
    #include <iostream>
    using namespace std;
    enum { FRAC_ADD, FRAC_SUB, FRAC_MUL, FRAC_DIV };
    
    class Fraction
    {
        public:
            Fraction();
            ~Fraction();
            void SetNum(BIGGEST_INT newnum);
            void SetDen(BIGGEST_INT newden);
            void SetAll(int Fracnum);
            BIGGEST_INT Num();
            BIGGEST_INT Den();
            void Reduce();
            void DisplayMixed();
            BIGGEST_INT GCD(int num1, int remainder);
            Fraction operator +(Fraction c);
            Fraction operator -(Fraction c);
            Fraction operator *(Fraction c);
            Fraction operator /(Fraction c);
            BIGGEST_INT num;
            BIGGEST_INT den;
    };
    
    Fraction Fraction_Do_Op(int operation, Fraction a, Fraction b);
    float FracDecVal(Fraction a);
    #endif
    
    //Fraction.cpp
    #include "Fraction.h"
    
    Fraction::Fraction() :num(0), den(1) {}
    
    Fraction::~Fraction(){}
    
    void Fraction::SetNum(BIGGEST_INT newnum){ num = newnum; }
    
    void Fraction::SetDen(BIGGEST_INT newden)
    {
        den = newden;
        if(den < 0)
        {
            num *= -1;
            den *= -1;
        }
    }
    
    void Fraction::SetAll(int Fracnum)
    {
        BIGGEST_INT newnum = 0, newden = 0;
        cout << "\nEnter a value for the numerator   in Fraction " << Fracnum << ": ";
        cin >> num;
        cout << "\nEnter a value for the denominator in Fraction " << Fracnum << ": ";
        cin >> den;
    
        if(den < 0)
        {
            num *= -1;
            den *= -1;
        }
    }
    
    BIGGEST_INT Fraction::Num(){ return(num); }
    
    BIGGEST_INT Fraction::Den(){ return(den); }
    
    void Fraction::Reduce()
    {
        BIGGEST_INT rdc = 0;
        if(den > num)
            rdc = GCD(den, num);
        else if(den < num)
            rdc = GCD(num, den);
        else
            rdc = GCD(num, den);
        num /= rdc;
        den /= rdc;
    }
    
    void Fraction::DisplayMixed()
    {
        cout << "Mixed value: " << num / den << " " << num % den << "/";
        if(den < 0)
            cout << den * -1;
        else
            cout << den;
        cout << endl;
    }
    
    BIGGEST_INT Fraction::GCD(int num1, int remainder)
    {
        if(remainder == 0)
            return(num1);
        else {
            return(GCD(remainder,num1%remainder));
        }
    }
    
    Fraction Fraction::operator +(Fraction d)
    {
        Fraction retfrac;
        retfrac.num = (num * d.den) + (d.num * den);
        retfrac.den = d.den * den;
        return(retfrac);
    }
    
    Fraction Fraction::operator -(Fraction d)
    {
        Fraction retfrac;
        retfrac.num = (num * d.den) - (d.num * d.den);
        retfrac.den = d.den * den;
        return(retfrac);
    }
    
    Fraction Fraction::operator *(Fraction d)
    {
        Fraction retfrac;
        retfrac.num = num * d.num;
        retfrac.den = den * d.den;
        return(retfrac);
    }
    
    Fraction Fraction::operator /(Fraction d)
    {
        Fraction retfrac;
        retfrac.num = num * d.den;
        retfrac.den = den * d.num;
        if(retfrac.den < 0)
        {
            retfrac.num *= -1;
            retfrac.den *= -1;
        }
        return(retfrac);
    }
    
    //---------------------
    Fraction Fraction_Do_Op(int operation, Fraction a, Fraction b)
    {
        Fraction rc;
        switch (operation)
        {
        case FRAC_ADD:
            return (a + b);
        case FRAC_SUB:
            return (a - b);
        case FRAC_MUL:
            return (a * b);
        case FRAC_DIV:
            return (a / b);
        }
        rc.SetNum(1);
        rc.SetDen(1);
        return (rc);
    }
    
    float FracDecVal(Fraction a)
    {
        float fracnum = a.num;
        float fracden = a.den;
        float decval = fracnum / fracden;
        return(decval);
    }
    

    Hier noch ein Anwendungsprogramm:

    // Allow the integer type to be changed on the fly
    #define BIGGEST_INT int
    
    #include <iostream>   // Include C++ input/output functions
    #include <cstdio>     // Include C input/output functions
    #include <conio.h>    // Include for pausing the program
    #include <cstdlib>    // Include C Library functions
    #include "Fraction.h" // Includes the Fraction Header File
    using namespace std;
    
    int main()
    {
        Fraction a;
        Fraction b;
        Fraction c;
        int op;
        char opchar;
    
        a.SetAll(1);
        b.SetAll(2);
    
        cout << endl << "Enter an operation to perform { + - / * } ";
        cin >> opchar;
    
        switch (opchar)
        {
        case '+':
            op = FRAC_ADD;
            break;
        case '-':
            op = FRAC_SUB;
            break;
        case '*':
            op = FRAC_MUL;
            break;
        case '/':
            op = FRAC_DIV;
            break;
        default:
            cout << "Invalid operator." << endl;
            return(0);
        }
        if((a.den == 0) || (b.den == 0))
            cout << "\n\nFraction is undefined.";
        else
        {
            c = Fraction_Do_Op(op, a, b);
            cout << "\n\n";
            c.Reduce();
            cout << "Fraction value: " << c.num << "/" << c.den << endl;
            cout << "Decimal value: " << FracDecVal(c) << endl;
            c.DisplayMixed();
        }
        cout << "Press any key to continue" << endl;
    
        getch();
    }
    


  • http://www.swox.com/gmp/

    High-level rational arithmetic functions (mpq). This category consists of about 35 functions, but all signed integer arithmetic functions can be used too, by applying them to the numerator and denominator separately.

    ist vielleicht etwas monströs für einfache anwendungen, aber geben tuts das.



  • @Erhard
    Ist nichts perönliches, aber dein Code ist IMHO totaler Schrott. Du verstößt gegen so jedes Prinzip, was es in C++ für gutes Design gibt. Was mich sehr verwundert, da du ansonsten ja gerne dabei bist zB. auf gotw zu verlinken. Shlos Klasse ist da deutlich besser!

    Dein Code ist noch nichtmal Standardkonform



  • kingruedi schrieb:

    @Shlo
    ich würde entweder implizite Casts von int zu fraction zulassen, was aber einige Risiken mit sich bringen kann oder noch Operatoren, die int als zweiten Parameter erwarten schreiben, damit man

    ab+cabc\frac{a}{b}+c \frac{a}{b}*c \ldots

    errechnen kann.

    Und num würde ich nicht unbedingt als default mit 1 belegen.

    Bei eins im Zähler und Nenner kann man nichts falsch machen, deshalb habe ich erstmal so gelassen 😉 Über implizite Umwandlung, oder eher neue Operatoren, sollte sich der Threadersteller Gedanken machen.



  • Ist bei einem Ctor mit mehr als einem Parameter das Schlüsselwort explicit nicht überflüssig?



  • Nicht mit default-Parametern



  • gcd() hat gefälligst private zu sein, genauso wie die Datenelemente ⚠



  • kingruedi schrieb:

    @Erhard
    Ist nichts perönliches, aber dein Code ist IMHO totaler Schrott. Du verstößt gegen so jedes Prinzip, was es in C++ für gutes Design gibt. Was mich sehr verwundert, da du ansonsten ja gerne dabei bist zB. auf gotw zu verlinken. Shlos Klasse ist da deutlich besser!

    Dein Code ist noch nichtmal Standardkonform

    Das ist doch gar nicht von Erhard! Hat er doch nur kopiert von der Seite!



  • Das hier läuft auch einwandfrei:

    Header:

    #ifndef _FRAC_
    #define _FRAC_
    
    #include <iostream>
    #include <string>
    
    class frac
    {
    private:
    	long m_zaehler, m_nenner;
    	long ggt(long, long);
    	void kuerzen(const long, const long);
    public:
    	frac(long z = 0, long n = 1);
    	const float frac::getq();
    	const std::string frac::getstr();
    	frac operator-() const
    	{
    		return frac(-m_zaehler, m_nenner);
    	}
    	frac& operator+=(const frac& a)
    	{
    		m_zaehler = a.m_zaehler * m_nenner + m_zaehler * a.m_nenner;
    		m_nenner *= a.m_nenner;
    		kuerzen(m_zaehler, m_nenner);
    		return *this;
    	}
    	frac& operator-=(const frac& a)
    	{
    		*this += -a;
    		kuerzen(m_zaehler, m_nenner);
    		return *this;
    	}
    	frac& operator++() //Präfix
    	{
    		m_zaehler += m_nenner;
    		kuerzen(m_zaehler, m_nenner);
    		return *this;
    	}
    	frac& operator++(int empty) //Postfix
    	{
    		m_zaehler += m_nenner;
    		kuerzen(m_zaehler, m_nenner);
    		return *this;
    	}
    	frac& operator--() //Präfix
    	{
    		m_zaehler -= m_nenner;
    		kuerzen(m_zaehler, m_nenner);
    		return *this;
    	}
    	frac& operator--(int empty) //Postfix
    	{
    		m_zaehler -= m_nenner;
    		kuerzen(m_zaehler, m_nenner);
    		return *this;
    	}
    	frac& operator*=(const frac& a)
    	{
    		m_zaehler = a.m_zaehler * m_zaehler;
    		m_nenner = a.m_nenner * m_nenner;
    		kuerzen(m_zaehler, m_nenner);
    		return *this;
    	}
    	frac& operator/=(const frac& a)
    	{
    		m_zaehler = a.m_nenner * m_zaehler;
    		m_nenner = a.m_zaehler * m_nenner;
    		if (m_nenner < 0)
    		{
    			m_zaehler = -m_zaehler;
    			m_nenner = -m_nenner;
    		}
    		kuerzen(m_zaehler, m_nenner);
    		return *this;
    	}
    	friend frac operator+ (const frac&, const frac&);
    	friend frac operator- (const frac&, const frac&);
    	friend frac operator* (const frac&, const frac&);
    	friend frac operator/ (const frac&, const frac&);
    	friend std::ostream& operator<< (std::ostream& os, const frac& a);
    	friend std::istream& operator>> (std::istream& is, frac& a);
    };
    
    #endif
    

    Implementation:

    #include <iostream>
    #include <sstream>
    #include "fraction.h"
    
    using namespace std;
    
    frac::frac(long z, long n)
    {
    	if (n < 0)
    	{
    		z = -z;
    		n = -n;
    	}
    	m_zaehler = z;
    	m_nenner = n;
    }
    
    const float frac::getq()
    {
    	return static_cast<float>(m_zaehler) / static_cast<float>(m_nenner);
    }
    
    const string frac::getstr()
    {
    	kuerzen(m_zaehler, m_nenner);
    	ostringstream z, n;
    	z <<m_zaehler;
    	n <<m_nenner;
    	if (m_zaehler == 0) //statt 0/1 soll 0 zurück gegeben werden
    		return "0";
    	else if (m_nenner  == 1) //statt 3/1 soll 3 zurück gegeben werden
    		return z.str();
    	else if (m_zaehler == m_nenner) //statt 3/3 soll 1 zurück gegeben werden
    		return "1";
    	else
    		return z.str() + "/" + n.str();
    }
    
    long frac::ggt(long z, long n)
    {
    	//z soll größer als n sein
    	z = abs(z);
    	n = abs(n); 
    	if (n > z)
    		return ggt(n, z);
    	else if (n != 0)
    		return ggt(n, z % n);
    	else
    		return z; //z ist Hauptnenner
    }
    
    void frac::kuerzen(const long z, const long n)
    {
        m_zaehler /= ggt(z, n);
        m_nenner /= ggt(z, n);
    }
    
    frac operator+(const frac& a, const frac& b)
    {
    	frac temp;
    	temp.m_nenner = a.m_nenner * b.m_nenner;
    	temp.m_zaehler = a.m_zaehler * b.m_nenner + b.m_zaehler * a.m_nenner;
    	temp.kuerzen(temp.m_zaehler, temp.m_nenner);
    	return temp;
    }
    
    frac operator-(const frac& a, const frac& b)
    {
    	frac temp = a;
    	temp += -b;
    	temp.kuerzen(temp.m_zaehler, temp.m_nenner);
    	return temp;
    }
    
    frac operator*(const frac& a, const frac& b)
    {
    	frac temp;
    	temp.m_zaehler = a.m_zaehler * b.m_zaehler;
    	temp.m_nenner = a.m_nenner * b.m_nenner;
    	temp.kuerzen(temp.m_zaehler, temp.m_nenner);
    	return temp;
    }
    
    frac operator/(const frac& a, const frac& b)
    {
    	frac temp;      
    	temp.m_zaehler = a.m_zaehler * b.m_nenner;
    	temp.m_nenner = a.m_nenner * b.m_zaehler;
    	if (temp.m_nenner < 0)
    	{
    		temp.m_zaehler = -temp.m_zaehler;
    		temp.m_nenner = -temp.m_nenner;
    	}
    	temp.kuerzen(temp.m_zaehler, temp.m_nenner);
    	return temp;
    }
    
    ostream& operator<<(ostream& os, const frac& a)
    {
    	frac temp;
    	temp.m_zaehler = a.m_zaehler;
    	temp.m_nenner = a.m_nenner;
    	temp.kuerzen(temp.m_zaehler, temp.m_nenner);
    	if (temp.m_zaehler == 0) //statt 0/1 soll 0 ausgegeben werden
    		os <<"0";
    	else if (temp.m_nenner == 1) //statt 3/1 soll 3 ausgegeben werden
    		os <<temp.m_zaehler;
    	else if (temp.m_zaehler == temp.m_nenner) //statt 3/3 soll 1 ausgegeben werden
    		os <<"1";
    	else
    		os <<temp.m_zaehler <<"/" <<temp.m_nenner;
    	return os;
    }
    
    istream& operator>>(istream& is, frac& a)
    {
    	is >>a.m_zaehler;
    	is >>a.m_nenner;
    	if (!is) 
    		return is;
    	if (a.m_nenner < 0)
    	{
    		a.m_zaehler = -a.m_zaehler;
    		a.m_nenner = -a.m_nenner;
    	}
    	return is;
    }
    

    main:

    #include <iostream> 
    #include "fraction.h" 
    
    using namespace std; 
    
    int main() 
    { 
        const int m_zeilen = 2; 
        const int m_spalten = 3; 
    
        frac m_arr[m_zeilen][m_spalten] = 
        { 
            frac(16,4), frac(0,1), frac(17,4), 
            frac(3,1),  frac(4,4), frac(-3,12) 
        };    
    
        frac a; 
        frac b(12,9); 
        a = frac(-3,11); 
        frac c = a * b / m_arr[1][2]; 
    	c *= 33;
    	c++;
    	c--;
    	--c;
    	++c;
    	c *= 2;
    	c /= 3;
    	c /= frac(12,11);
        cout << c << endl; //=88/3
        return 0; 
    }
    

    Habs noch etwas erweitert.



  • Warum eigentlich nur die Präfix Inkrement/Dekrement-Operatoren? Aus Geschwindigkeitsgründen?



  • Hilfe! m_ 😞
    Ein klein geschriebener Klassenname.
    Wieso ist ggT nicht statisch, wenn es doch die beiden Zahlen kriegt?
    Wieso kriegt kuerzen() die beiden Zahlen, wenn es nicht statisch ist?



  • Und warum sind die arithm. Operatoren nicht über die X= definiert?



  • dein Code ist IMHO totaler Schrott.
    Das ist doch gar nicht von Erhard! Hat er doch nur kopiert von der Seite!

    @kingruedi: du solltest deinen Job als Moderator wirklich überdenken, wenn du keine Zeit hast, die Beiträge komplett zu lesen. 😃 😉

    Ich habe den Code gepostet, damit man den Unterschied (Vorteile/Nachteile) zu Shlo's Code sieht. Nun liegt ja noch ein dritter Entwurf auf dem Tisch. Ein interessanter Wettbewerb. 😉 Bin auf kingruedi's Klasse gespannt.





  • template <typename IntType>
    class rational :
        less_than_comparable < rational<IntType>,
        equality_comparable < rational<IntType>,
        less_than_comparable2 < rational<IntType>, IntType,
        equality_comparable2 < rational<IntType>, IntType,
        addable < rational<IntType>,
        subtractable < rational<IntType>,
        multipliable < rational<IntType>,
        dividable < rational<IntType>,
        addable2 < rational<IntType>, IntType,
        subtractable2 < rational<IntType>, IntType,
        subtractable2_left < rational<IntType>, IntType,
        multipliable2 < rational<IntType>, IntType,
        dividable2 < rational<IntType>, IntType,
        dividable2_left < rational<IntType>, IntType,
        incrementable < rational<IntType>,
        decrementable < rational<IntType>
        > > > > > > > > > > > > > > > >
    

    Sieht zumindest witzig aus... 🙄



  • fehlt da nicht public oder ein andres keyword? 😕



  • http://www.boost.org/libs/rational/rational.html#Interface

    Bei boost gibt es eben (fast) alles. 🙂

    #include <iostream>
    #include <conio.h>
    #include <boost/rational.hpp>
    using namespace std;
    using namespace boost;
    
    int main()
    {
      rational<int> a,b,c;
      cout << "Bruch 1:  "; cin >> a;
      cout << "Bruch 2:  "; cin >> b;
      c = a*b;
      cout << "Resultat: " << c << " = " << rational_cast<double>(c);
      getch();
    }
    

    Ausgabe:
    Bruch 1: 2/33
    Bruch 2: 5/55
    Resultat: 2/363 = 0.00550964

    Der Nachteil bei boost ist allerdings, dass man nicht auf einfache Weise selbst die Klasse verändern kann.



  • otze schrieb:

    fehlt da nicht public oder ein andres keyword? 😕

    Wenn es eine private-Ableitung ist, warum sollte es?


Anmelden zum Antworten