Hallo Martin, On Tue, Jul 14, 2009 at 08:19:30AM +0200, to...@tuxteam.de wrote: > Ich muss Micha recht geben. Ich erwarte hier keine wilden Operationen, > die solch eine Zahl rechtfertigen würden (im Wesentlichen geteilt durch > Hundert bei der Dezimal -> Intern Wandlung, und das müsste eine > Bruchlibrary wie libgmp verlustfrei können: 36154/100, ggf. gekürzt). Es > sieht also aus, als wäre die arme Zahl durch eine float-Mangel gezogen > worden.
Korrekt, und zwar von AqBanking selbst in der Funktion AB_Value_fromString(), die den übergebenen String bisher zunächst in eine Gleitkommazahl (der Typ mpf_t aus der Bibliothek GMP ist eine Gleitkommazahl mit beliebiger Präzision!) umwandelt, um erst dann eine rationale Zahl daraus zu machen. Ich habe gerade einen Patch geschrieben, der eine Zahl, die so normalerweise in Strings übergeben wird, korrekt in ein mpq_t (rationale Zahl aus libGMP) umwandelt (im Anhang die Datei proper_rational_number_AB_Value.patch). Dazu habe ich ein kleines Testprogramm geschrieben, dass mit dem folgenden Befehl in eine ausführbare Datei übersetzt werden kann: gcc $(pkg-config --cflags --libs aqbanking) -o ab_value_test ab_value_test.c Anhand dieses Beispiel-Programms kann man gut nachvollziehen, wie mit dem ungepatchten AqBanking aus der Zahl 361,54 intern die unsägliche Zahl 28644149875407128613569879804477/79228162514264337593543950336 wird. Mit dem Patch wird die Zahl hingegen als einfache rationale Zahl 36154/100 gespeichert. Zusätzlich behebt der Patch einen kleinen, sehr selten auftretendes Speicherleck: free(tmpString) wurde unter bestimmten Umständen nicht aufgerufen. Zu guter letzt habe ich die ganzen API-Funktionen, die irgendwie mit Gleitkommazahlen agieren als veraltet (deprecated) markiert, damit man beim Übersetzen mal sieht, wo überall dieser Mist verwendet wird und damit man evtl. irgendwann mal gänzlich auf die Gleitkommazahlen verzichten kann. Auch die Funktionen für Multiplikation und Division von AB_VALUEs habe ich als veraltet markiert, da sie zum einen nicht wirklich sinnvoll sind (Was ist denn 10€ * 10€? Korrekt wäre 100€²...) und zum anderen nach meinen Recherchen nicht benutzt werden. Ich würde mich freuen, wenn der Patch ins SVN kommt. Schöne Grüße Micha
Index: src/libs/aqbanking/types/value.c =================================================================== --- src/libs/aqbanking/types/value.c (Revision 1719) +++ src/libs/aqbanking/types/value.c (Arbeitskopie) @@ -90,7 +90,7 @@ AB_VALUE *AB_Value_fromString(const char *s) { AB_VALUE *v; const char *currency=NULL; - int rv; + int conversion_succeeded = 1; // assume conversion will succeed char *tmpString=NULL; char *p; char *t; @@ -128,26 +128,29 @@ if (t) *t='.'; - if (strchr(p, '.')) { - mpf_t v1; + if (t=strchr(p, '.')) { + // remove comma and calculate denominator + unsigned long denominator = 1; + char *next; + do { + next=t+1; + *t=*next; + if (*next != 0) + denominator *= 10; + t++; + } while (*next); - mpf_init(v1); - if (mpf_set_str(v1, p, 10)) { - DBG_ERROR(AQBANKING_LOGDOMAIN, "[%s] is not a valid value", s); - AB_Value_free(v); -#ifdef HAVE_SETLOCALE - setlocale(LC_NUMERIC, currentLocale); - free(currentLocale); -#endif - return NULL; + // set denominator to the calculated value + mpz_set_ui(mpq_denref(v->value), denominator); + + // set numerator to the resulting integer string without comma + if (mpz_set_str(mpq_numref(v->value), p, 10) == -1) { + conversion_succeeded = 0; } - mpq_set_f(v->value, v1); - mpf_clear(v1); - rv=1; } else { /*DBG_ERROR(0, "Scanning this value: %s\n", p);*/ - rv=gmp_sscanf(p, "%Qu", v->value); + conversion_succeeded = (gmp_sscanf(p, "%Qu", v->value) == 1); } #ifdef HAVE_SETLOCALE @@ -162,15 +165,12 @@ /* temporary string no longer needed */ free(tmpString); - if (rv!=1) { + if (!conversion_succeeded) { DBG_ERROR(AQBANKING_LOGDOMAIN, "[%s] is not a valid value", s); AB_Value_free(v); return NULL; } - /* canonicalize */ - mpq_canonicalize(v->value); - if (isNeg) mpq_neg(v->value, v->value); Index: src/libs/aqbanking/types/value.h =================================================================== --- src/libs/aqbanking/types/value.h (Revision 1719) +++ src/libs/aqbanking/types/value.h (Arbeitskopie) @@ -65,7 +65,7 @@ int prec, int withCurrency); -AQBANKING_API AB_VALUE *AB_Value_fromDouble(double i); +AQBANKING_API AQBANKING_DEPRECATED AB_VALUE *AB_Value_fromDouble(double i); /** Create a value from the given GWEN_DB. */ @@ -75,14 +75,14 @@ AQBANKING_API int AB_Value_toDb(const AB_VALUE *v, GWEN_DB_NODE *db); /** Write the given value into the given GWEN_DB (uses float instead of rational). */ -AQBANKING_API int AB_Value_toDbFloat(const AB_VALUE *v, GWEN_DB_NODE *db); +AQBANKING_API AQBANKING_DEPRECATED int AB_Value_toDbFloat(const AB_VALUE *v, GWEN_DB_NODE *db); /** * This function returns the value as a double. * You should not feed another AB_VALUE from this double, because the * conversion from an AB_VALUE to a double might be lossy! */ -AQBANKING_API double AB_Value_GetValueAsDouble(const AB_VALUE *v); +AQBANKING_API AQBANKING_DEPRECATED double AB_Value_GetValueAsDouble(const AB_VALUE *v); /** @@ -91,7 +91,7 @@ * the conversion from AB_VALUE to double to AB_VALUE might change the * real value. */ -AQBANKING_API void AB_Value_SetValueFromDouble(AB_VALUE *v, double i); +AQBANKING_API AQBANKING_DEPRECATED void AB_Value_SetValueFromDouble(AB_VALUE *v, double i); /** * Write the value (without the currency) in nominator/denominator @@ -111,8 +111,8 @@ AQBANKING_API int AB_Value_AddValue(AB_VALUE *v1, const AB_VALUE *v2); AQBANKING_API int AB_Value_SubValue(AB_VALUE *v1, const AB_VALUE *v2); -AQBANKING_API int AB_Value_MultValue(AB_VALUE *v1, const AB_VALUE *v2); -AQBANKING_API int AB_Value_DivValue(AB_VALUE *v1, const AB_VALUE *v2); +AQBANKING_API AQBANKING_DEPRECATED int AB_Value_MultValue(AB_VALUE *v1, const AB_VALUE *v2); +AQBANKING_API AQBANKING_DEPRECATED int AB_Value_DivValue(AB_VALUE *v1, const AB_VALUE *v2); AQBANKING_API int AB_Value_Negate(AB_VALUE *v);
#include <gwenhywfar/buffer.h> #include <aqbanking/banking.h> char *input = "361,54"; int main(int argc, char *argv[]) { AB_VALUE *value; GWEN_BUFFER *buf; if (argc > 1) input = argv[1]; value = AB_Value_fromString(input); buf = GWEN_Buffer_new(NULL, 300, 0, 0); AB_Value_toString(value, buf); printf("AqBanking stores %s internally as rational number %s\n", input, GWEN_Buffer_GetStart(buf)); GWEN_Buffer_free(buf); AB_Value_free(value); }
signature.asc
Description: Digital signature
------------------------------------------------------------------------------ Enter the BlackBerry Developer Challenge This is your chance to win up to $100,000 in prizes! For a limited time, vendors submitting new applications to BlackBerry App World(TM) will have the opportunity to enter the BlackBerry Developer Challenge. See full prize details at: http://p.sf.net/sfu/Challenge
_______________________________________________ Aqbanking-devel mailing list Aqbanking-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/aqbanking-devel