Klasse für Bruchzahlen gesucht
-
@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 manerrechnen 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(); }
-
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 manerrechnen 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.00550964Der 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.