Klasse für Bruchzahlen gesucht



  • 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?



  • fehlt da nicht public oder ein andres keyword?
    Wenn es eine private-Ableitung ist, warum sollte es?

    //...
    template <typename IntType>
    class rational :
      //...
    {
        typedef IntType int_type;
        typedef typename boost::call_traits<IntType>::param_type param_type;
    
    public:
        rational() : num(0), den(1) {}
        rational(param_type n) : num(n), den(1) {}
        rational(param_type n, param_type d) : num(n), den(d) { normalize(); }
    
        // Default copy constructor and assignment are fine
    
        // Add assignment from IntType
        rational& operator=(param_type n) { return assign(n, 1); }
    
        // Assign in place
        rational& assign(param_type n, param_type d);
    
        // Access to representation
        IntType numerator() const { return num; }
        IntType denominator() const { return den; }
    
        //...
    

    Wenn der Konstruktor private wäre, könnten wir wenig damit anfangen. 😉



  • "Hilfe! m_
    Ein klein geschriebener Klassenname."

    Was meinst du damit?



  • Erhard Henkes schrieb:

    Wenn der Konstruktor private wäre, könnten wir wenig damit anfangen. 😉

    Ehrlich gesagt verstehe ich den Zusammenhang nicht. Was hat das eine, mit dem anderen zu tun?



  • Probiers doch aus. 😉



  • Shlo schrieb:

    #include <iostream>
    	template<class E, class T, class A>
    	std::basic_string<E,T,A> to_string() const
    	{
    		typedef typename std::basic_string<E,T,A>::value_type value_type;
    		typedef typename std::basic_string<E,T,A>::traits_type traits_type;
    		typedef typename std::basic_string<E,T,A>::allocator_type allocator_type;
    
    		typedef std::basic_ostringstream<value_type,
    			traits_type,allocator_type> ostringstream;
    
    		ostringstream oss;
    		oss << *this;
    
    		return oss.str();
    	}
    

    wie verwendet man diese funktion? mach mal ein beispiel.

    fraction operator-() const
    {
    	return fraction(-num_,-den_);
    }
    

    einmal - wäre ok.


Anmelden zum Antworten