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

Reply via email to