Ein Zwischenruf zu den rationalen Zahlen: Am Donnerstag, 6. Dezember 2007 08:07 schrieb Thomas Baumgart: > > 6:2007/12/06 07-49-13:(null)(26600):mymoneybanking.cpp: 527: Adding > > transaction > > Value: > > 58184456566852616694523094906498756766597/4253529586511730793292182592897 > >10 26432 (1367.91 EUR) > > num = 5902958103587056517 (51eb851eb851eb85) > > denom = 504684641933067010 (701000206040302) > > MyMoneyMoney(5902958103587056517/504684641933067010) > > MyMoneyMoney(0,0000)
Wenn hier rationale Zahlen verarbeitet werden, *müssen* Zähler/Nenner natürlich alle Nase lang auch mal wieder gekürzt werden. Ansonsten wachsen die bei jeder arithmetischen Operation zu solchen Monstern an, bewirken dann wieder einen Werteüberlauf und man hat gegenüber double wieder nichts gewonnen. Hm, das gmp-Manual http://www.gnu.org/software/gmp/manual/html_node/Rational-Number-Functions.html behauptet, "All rational arithmetic functions assume operands have a canonical form, and *canonicalize their result*." - genau das ist ja gewünscht. Woher also kommen diese Zahlenmonster? Hm, die AB_Values werden doch letztlich entweder über mpq_set_d (bei FromDouble) oder mpq_set_f (bei fromString) erzeugt. (@Martin: Welches von beiden war's wohl hier?) Laut http://www.gnu.org/software/gmp/manual/html_node/Rational-Conversions.html sind diese wiederum explizit "without rounding". *Das* wiederum könnte hier der Grund sein: Wenn nämlich beim Erzeugen des gmp-floats brav als Basis die 2 angenommen wird, dann wird auch eine harmlose Dezimalzahl wie 1.3 ein ellenlanger Bruch. Weil sich 1 + 3/10 nicht in endlicher Stellenanzahl als floating point der Basis 2 darstellen lässt. Anders sähe das aus, wenn das eine floating point-Zahl der Basis 10 wäre. @Martin: Da solltest du lieber etwas anderes in Value_fromString benutzen. Wenn der Umweg über den gmp-float sein soll, dann nach Möglichkeit lieber mit der Funktion mpf_inp_str() http://www.gnu.org/software/gmp/manual/html_node/I-O-of-Floats.html und explizit angegebener Basis 10. Oder noch besser, man würde den string manuell als zwei Integers einlesen. Etwa dieser Pseudocode: int int1, int2, nachkommastellen; mpq_t rational1, rational2, result; // Anzahl Nachkommastellen aus Abstand vom Dezimalpunkt // zum nachfolgenden space (benötigt noch weitere sanity checks) nachkommastellen = strchr(' ', input) - strchr('.',input); // Vor- und Nachkommastellen lesen scanf(input, "%d.%d", int1, int2); // Dies sind die Vorkommastellen mpq_set_si(rational1, int1, 1); // und dies die Nachkommastellen, und natürlich // 10 hoch nachkommastellen irgendwie, ggf. über mpz_t ausrechnen lassen mpq_set_ui(rational2, int2, 10^nachkommastellen); // Ergebnis mpq_add(result, rational1, rational2); Wenn meine Vermutung hier stimmt, dann müsste bisher das Einlesen von "1.3" über AB_Value_fromString() momentan einen ellenlangen Bruch ergeben, aber beim Umstieg auf sowas wie eben erklärt würde dann das erwartete "13/10" herauskommen. > Kleiner Nachtrag noch (der erste Kaffee wirkt noch nicht so richtig und der > Finger war schon auf dem Send Button). Der Umweg über ein 'double' > funktioniert: Für einen Im-/Export finde ich den "Umweg" über double sogar relativ empfehlenswert. Das ganze Buhei mit den rationalen Zahlen (obwohl in gnucash und hier Fixpoint-Zahlen bereits ausreichend gewesen wären, aber nu isses zu spät) bezieht sich doch nur auf arithmetische Operationen, also dass nach dem Addieren von zig Buchungen nachher trotzdem das richtige rauskommt. Das gibt bei double-Werten Ärger, weil Rundungen zur Basis 2 was anderes ergeben als die im Finanzwesen erwarteten Rundungen zur Basis 10 und die akkumulieren sich halt bei zig arithmetischen Operationen. Aber beim Im-/Export gibt es die Rundung nur einmal, so dass sie sich eben nicht akkumuliert, und da reicht die Maschinengenauigkeit vom double dicke aus (genauer: für 15-16 Dezimalstellen, also Geldbeträge bis 10^13€, also 10 Billionen), um keine unerwarteten Rundungsfehler reinzubekommen. Eine Nebenbemerkung noch zur Länge von long int, weil ich mich neulich selbst damit herumschlagen musste: > Das Poblem ist, dass gmp intern nicht wie Ihr mit 64-Bit langen Zahlen > arbeitet, sondern mit nahezu unbegrenzt langen... > > Zurueckliefern will es aber nur long int (LAenge je nach System, meistens > aber wohl 32 bit). Auf 64bit-Linux mit gcc hat sich "long int" sozusagen ohne Vorwarnung in 64 bit verwandelt; bei anderen Compilern kann das auch so sein, muss aber nicht. "int" blieb bei 32 bit. Für jenes Projekt war das seinerzeit nervig genug, um auf z.B. boost::int32_t und boost::int64_t umzusteigen, sobald wir den Wertebereich und damit die Länge ausdrücklich wissen mussten. Gruß Christian ------------------------------------------------------------------------- SF.Net email is sponsored by: The Future of Linux Business White Paper from Novell. From the desktop to the data center, Linux is going mainstream. Let it simplify your IT future. http://altfarm.mediaplex.com/ad/ck/8857-50307-18918-4 _______________________________________________ Aqbanking-devel mailing list Aqbanking-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/aqbanking-devel