Schwachsinniger Code für Zwischendurch
-
Hallo,
ich habe mich heute mal wieder um meine echten Probleme rumgedrückt und stattdessen an einer Lösung für ein Problem gebastelt, das keins ist.Ich hantiere an einigen Stellen in meinem Code mit ziemlich große Geldbeträgen.
D.h. ich habe häufig sowas wie:
Money price = 2000000000.0;
oder:
Money salary = 2000000.0;Nun neige ich dazu mich bei den vielen Nullen zu vertippen, also wollte ich die magischen Zahlen durch symbolische Konstanten ersetzen. So in etwa:
Money price = 2 Billion;Die Syntax habe ich aber für kompliziertere Sachen (wie z.B. 2 Billion 3 Million) schon mal nicht hinbekommen. Aus irgendeinem für mich jetzt nicht mehr nachvollziehbarem Grund habe ich dann den ganzen Tag mit einer Templatelösung rumgespielt, die am Ende folgendes erlaubt:
Money salary = !(2.0, Billion, 1.0, Million);
bzw.
Money salary = (2.0, Billion, 1.0, Million).eval();Der Code sieht so aus:
#include <assert.h> template <bool> struct StaticAssert; template <> struct StaticAssert<true>{}; template <class T> struct Multiplier { public: enum {id = T::id}; double eval() const { return T::eval(); } }; struct Thousand_t { enum {id = 0}; static double eval() { return 1000.0; } }; struct Million_t { enum {id = 1}; static double eval() { return 1000000.0; } }; struct Billion_t { enum {id = 2}; static double eval() { return 1000000000.0; } }; Multiplier<Thousand_t> Thousand; Multiplier<Million_t> Million; Multiplier<Billion_t> Billion; class Value { public: explicit Value(double d) : value_(d) {} double eval() const { return value_; } double getValue() const { return value_; } private: double value_; }; template <class Left, class Right> class Mult { public: Mult(Left l, Right r) : left_(l), right_(r) {} double eval() const { double left = left_.eval(); double right = right_.eval(); return left * right; } Left getLeft() const { return left_; } Right getRight() const { return right_; } typedef Left LeftType; typedef Right RightType; private: Left left_; Right right_; }; template <class Left, class Right> class Add { public: Add(Left l, Right r) : left_(l), right_(r) {} double eval() const { double left = left_.eval(); double right = right_.eval(); return left + right; } Left getLeft() const { return left_; } Right getRight() const { return right_; } typedef Left LeftType; typedef Right RightType; private: Left left_; Right right_; }; template <class T> struct GetMaxId { typedef typename T::LeftType Left; typedef typename T::RightType Right; enum { maxId = GetMaxId<Left>::maxId > GetMaxId<Right>::maxId ? GetMaxId<Left>::maxId : GetMaxId<Right>::maxId }; }; template<> struct GetMaxId<Value> { enum {maxId = -1}; }; template<> struct GetMaxId<Multiplier<Billion_t> > { enum {maxId = Billion_t::id}; }; template<> struct GetMaxId<Multiplier<Million_t> > { enum {maxId = Million_t::id}; }; template<> struct GetMaxId<Multiplier<Thousand_t> > { enum {maxId = Thousand_t::id}; }; template <class T> Add<Mult<Value, Multiplier<T> >, Value> operator,(double d, Multiplier<T> t) { return Add<Mult<Value, Multiplier<T> >, Value>( Mult<Value, Multiplier<T> >(Value(d), t), Value(0) ); } template <class T> Add<T, Value> operator,(Add<T, Value> e, double z) { return Add<T, Value>(e.getLeft(), Value(z)); } template <class T, class U> Add<T, Mult<Value, Multiplier<U> > > operator,(Add<T, Value> e, Multiplier<U> m) { { StaticAssert<(GetMaxId<T>::maxId > Multiplier<U>::id) > ERROR_NOT_ALLOWED; (void)ERROR_NOT_ALLOWED; } double temp = e.getRight().getValue(); return Add<T, Mult<Value, Multiplier<U> > >(e.getLeft(), Mult<Value, Multiplier<U> >(Value(temp), m)); } template <class T, class U, class V> Add<Add<T, Mult<U, V> >, Value> operator,(Add<T, Mult<U, V> > e, double z) { return Add<Add<T, Mult<U, V> >, Value>(e, Value(z)); } template <class T, class U> double operator!(Mult<T, U> m) { return m.eval(); } template <class T, class U> double operator!(Add<T, U> m) { return m.eval(); } int main() { double t1 = !(1.0,Thousand); double m1 = !(1.0,Million); double b1 = (1,Billion).eval(); assert(t1 == 1000.0); assert(m1 == 1000000.0); assert(b1 == 1000000000.0); double t2 = !(1.0,Thousand,100.0); double m2 = !(1.0,Million,100.0); double b2 = !(1.0,Billion,100.0); assert(t2 == 1100.0); assert(m2 == 1000100.0); assert(b2 == 1000000100.0); double m3 = !(1.0,Million,1.0,Thousand); assert(m3 == 1001000.0); double m4 = !(1.0,Million,1.0,Thousand,1.0); assert(m4 == 1001001.0); double b3 = !(1.0,Billion,1.0,Thousand); assert(b3 == 1000001000.0); double b4 = !(1.0,Billion,1.0,Thousand,1.0); assert(b4 == 1000001001.0); double b5 = !(1.0,Billion,1.0,Million); assert(b5 == 1001000000.0); double b6 = !(1.0,Billion,1.0,Million,1.0); assert(b6 == 1001000001.0); double b7 = !(1.0,Billion,1.0,Million,1.0,Thousand); assert(b7 == 1001001000.0); double b8 = !(1.0,Billion,1.0,Million,1.0,Thousand,1.0); assert(b8 == 1001001001.0); // Wird korrekterweise als Fehlerhaft gemeldet: !(1.0,Billion, 1.0,Million, 1.0,Billion); !(1.0,Thousand,1.0,Million); }Er ist an einigen Stellen absichtlich etwas umständlicher, damit auch der VC 6.0 damit zurecht kommt (hier muss man allerdings einen anderen als den Komma-Operator verwenden. Z.B. operator|).
Warum belästige ich das Forum damit?
Ganz einfach. Mir scheint ich habe mal wieder den schwachsinnigsten Weg gewählt und diesen möglichst kompliziert und inellegant implementiert.Ich habe das sichere Gefühl, dass es eine viel einfachere und ellegantere Lösung gibt.
Z.B. durch die geschickte Anwendung des Präprozessors.
Würde mich also über Hinweise freuen.
-
mal ne bloede frage:
was spricht gegen
2*Million+3*Thousand+7*Hundred+9
liest sich IMHO auch nicht schlechter als
!(1.0,Billion,1.0,Million,1.0);klar, ist nicht so geil templatetisiert, sondern einfach nur konstanten...
aber ich koennte mir ja auch ein
Million(3)+Thousand(7)+Plain(6)
vorstellen - aber irgendwie hapert die syntax immer...es waere besser, wenn mann
2_000_000 schreiben koennte (@Ruby schaue)
ideal waere wohl ein
Number(2,356,000);
geht aber leider nicht - man muesste die fuehrenden 0 irgendwie wegbekommen...uU mit
Number(2,.356,.000); ?
aber das waere auch doofne, also ich finde keine vernuenftige loesung

mir fallen zwar loesungen ein - aber das problem ist die anwendung, denn diese sollte ja intuitiv sein.
-
Wie wäre es wenn du es anders machst.
2 Zahlen, sagen wir number, und number_null.
number ist die 'Zahl' die in der Zahl ist. Also bei 1000 1 oder bei 12450 wärs 1245.
und bei number_null gibst du einfach die zahl der Nullen an. also bei 1000 wär das dann 3...Devil
-
#define Millionen * (1000000.0) #define Thousand * (1000.0) #define Hundred * (100.0) float zahl = 2 Millionen + 3 Thousand + 5 Hundred;this way ?
gruß
-
@nameless:
ah, das gefaellt mir sehr gut.
-
@nameless
Exakt die selben defines habe ich auch ausprobiert.Das einzige was mich daran stört ist, dass ich hier beliebig mischen kann:
double d = 1 Hundred + 1 Thousand; -> Das könnte man als Hunderttausend interpretieren, deshalb führt sowas in meinem Ansatz zu einem Compiler-Fehler.
Genauso wie die Wiederholung:
double d = 1 Hundred + 1 Hundred + 1 Million + 1 Hundred;Das ist irgendwie unintuitiv. Die Templates zwingen mich immer zu einer absteigenden Anordnung ohne Wiederholung.
Naja, vergleicht man aber Aufwand und Nutzen, dann ist die define-Variante weit vorn

-
HumeSikkins schrieb:
Money price = 2000000000.0;
oder:
Money salary = 2000000.0;
Nun neige ich dazu mich bei den vielen Nullen zu vertippen, also wollte ich die magischen Zahlen durch symbolische Konstanten ersetzen. So in etwa:
Money price = 2 Billion;ja, das würde mich auch stören. deswegen hab ich auswendig gelernt, daß ne milliarde 9 und ne million 6 hat.
Money price = 2e9;
oder:
Money salary = 2e6;
man kann damit sogar in maßen mischen!
Money salery = 2.345e6;
oder für freaks:
Money Salery = 2e9 + 345e6;
-
Shade Of Mine schrieb:
Number(2,356,000);
geht aber leider nicht - man muesste die fuehrenden 0 irgendwie wegbekommen...Ich denke, es geht: Zwischen zwei Kommas stehen immer 3 Stellen. Man muss also mit dem ganzen va_arg-Scheiß die "Ziffern" im 1000-er-System auswerten. Oder ohne va_arg geht wohl auch. Mit entsprechend überladenen Operatoren:
Number/2,000,000. Die Klasse muss natürlich auch operator int() etc. haben...
Der Fantasie sind keine Grenzen gesetzt.
-
So. Jetzt hab ich das auch implementiert...
#include <iostream> using namespace std; class Number_t { int v; public: Number_t(int vv = 0) : v(vv) {} Number_t operator/(int o) { return o; } Number_t operator,(int o) { return v * 1000 + o; } friend ostream &operator<<(ostream &s, Number_t n) { s << n.v; } operator int() { return v; } } Number; int main() { cout << (Number/25,000,456) << endl; }Und wer jetzt Bedenken wegen der Performance hat... der Compiler ist wunderbar in der Lage, das zu wegzuoptimieren.
-
HumeSikkins schrieb:
Das einzige was mich daran stört ist, dass ich hier beliebig mischen kann:
double d = 1 Hundred + 1 Thousand; -> Das könnte man als Hunderttausend interpretierensorry aber die argumentation versteh ich nicht...
wenn ich "1 Hundred + 1 Thousand" stehen seh dann is das für mich ne rechnung deren ergebniss 1100 ist und das als hundertausend zu interpretieren ist doch sehr weit hiergeholt, oder?
die makros erlauben es ja sogar "1 Hundred Thousand" zu schreiben und es kommt 100'000 raus wie man er erwarten würde.
-
sorry aber die argumentation versteh ich nicht...
Keiner verlangt von dir meinen beschränkten Verstand zu verstehen.
Und das er beschränkt ist, hat mir Volkard mal wieder eindrücklich gezeigt. Seine Lösung ist genau die Offensichtliche, deren Existenz ich vermutete, die ich aber nicht finden konnte.
-
Hat jemand außer volkard schon meinen Denkfehler entdeckt?
-
int i = 2; cout << ((Number/25,000,000) / i) << endl;Liefert irgendwie nicht das Erwartete.
-
Hat jemand außer volkard schon meinen Denkfehler entdeckt?
Du dachtest, du hättest funktionsfähigen C++ Code gepostet?

-
nee, viel lustiger.
cout << (Number/2,468,024) << endl;das ding gibt nicht 2468024 aus.
-
HumeSikkins schrieb:
Du dachtest, du hättest funktionsfähigen C++ Code gepostet?

Du dachtest ich hätte gedacht?

-
volkard schrieb:
cout << (Number/2,468,024) << endl;das ding gibt nicht 2468024 aus.
*plonk*
Logisch.
-
Und das er beschränkt ist, hat mir Volkard mal wieder eindrücklich gezeigt. Seine Lösung ist genau die Offensichtliche, deren Existenz ich vermutete, die ich aber nicht finden konnte.
Oh. Ich dachte am anfang du würdest das ausschreiben, weil dir diese schreibweise nicht gefällt.
Falls Geschwindigkeit nicht ganz so wichtig ist, könnte man ja auch einen string paresen. Dann hat man sowas Money("2.332.1213,12") oder so.
-
Was haltet ihr davon:
#include <iostream> #include <cassert> using namespace std; class Number { public: Number(unsigned int n) : _n(n) {} Number operator()(Number i) const { assert(i._n <= _n); return _n + i._n; } unsigned int operator()(unsigned int i = 0) const { assert(i < 1000); return _n + i; } private: const unsigned int _n; }; template <int n> class Number_temp {}; template <int n> Number operator*(int i, Number_temp<n>) { assert(i < 1000); return n * i; } #define Billion * Number_temp<1000000000>())( #define Million * Number_temp<1000000>())( #define Thousand * Number_temp<1000>())( int main() { // Funktioniert unsigned int MyNumber1 = Number(1 Billion 20 Million 24 Thousand 123); unsigned int MyNumber2 = Number(123 Million); // Wird als falsch erkannt: unsigned int MyNumber3 = Number(50 Million 1 Billion); // Auch falsch unsigned int MyNumber4 = Number(1 Billion 20 Million 24 Thousand 1892); // Auch falsch unsigned int MyNumber5 = Number(1 Billion 1 Billion 24 Thousand 123); // Auch falsch unsigned int MyNumber5 = Number(1 Billion 5950 Thousand 123); cout << MyNumber1 << endl; return 0; }Die Zahleneingabe ist intuitiv wie bei der reinen Makro-Variante, plus eine Fehlererkennung.

Falsche Zahlen werden allerdings erst zur Laufzeit durch die Asserts erkannt.
-
Vielleicht geht's ja wirklich nur mir so, aber ich finde "123456789.12" ja noch übersichtlicher als "123 Milion 456 Thousand 769.12". Und da eine englische eingaber scheibar gefordert ist kann man meinen vorgeschlagenen Parser ja anpassen Money("123,456,789.12").
Vor allem solche angaben sind doch bei euch immer noch möglich: 1 Million 5000 Thousand. Soll heißen 6 Millionen.
-
@Helium
Die String-Variante ist in meiner Money-Klasse bereits drin. Natürlich mit wählbarem Grouping und Thousand-Seperator