Unerwünschtes Runden

Alles rund um die Programmierung mit Qt
Antworten
AlexDu
Beiträge: 14
Registriert: 28. Januar 2009 14:08

Unerwünschtes Runden

Beitrag von AlexDu »

Guten Abend,

nach ein paar Stunden Fehlersuche hab ich jetzt etwas gefunden, dass ich mir nicht erklären kann.

Code: Alles auswählen

    double d = 12345.67;

    qDebug() << "test1" << d;
    qDebug() << "test2" << QString::number(d,'f',2);
ergibt folgende Ausgabe:

Code: Alles auswählen

test1 12345.7 
test2 "12345.67" 
Das Problem tritt nicht nur bei qDebug auf, sondern die Zahlen werden auch so weitergegeben (z.B. in die Datenbank).
Bis zu 4 Stellen vor dem Komma klappt das einwandfrei.

Alex
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Und wo ist da jetzt das Problem? Du gibst einer Funktion einen float-Wert ohne eine Angabe wie er ihn zu behandeln hat - was erwartest Du?
Und DB - was für einen Datentyp hat die Spalte, welcher DB-Treiber, ...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
kater
Beiträge: 306
Registriert: 29. Dezember 2009 01:13
Wohnort: Darmstadt

Beitrag von kater »

Wiso behandeln? In ein Double passen 15 Stellen oder so und er hat grad mal 7. Was für Informationen sollte man den Computer denn noch geben.
double d = 12345.67;
qDebug() << "test1" << d;
std::cerr << "test2 " << d << "\n";
fprintf(stderr, "test3 %f\n", d);
Ausgabe:

test1 12345.7
test2 12345.7
test3 12345.670000

PS. warum gibt der QtCreator unten bei Application Output nur den Error Stream aus? Dann seh ich ja nie meine Normalen Programmausgaben :(

grrr Qt suckt, C++ suckt, C <3 Darum liebe ich es. Weil C das tut was man erwartet. Die verd***te Zahl so ausgeben wie ich sie eingegeben habe ^^
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Bevor man sich so arg in Herzinfarkgefahr begibt, sollte man sich kluglesen:
http://www.cplusplus.com/reference/iost ... ors/fixed/
Vor alle dieser Zusatz:
Notice that the treatment of the precision field differs between the default floating-point notation and the fixed and scientific notations. On the default floating-point notation, the precision field specifies the maximum number of meaningful digits to display both before and after the decimal point, while in both the fixed and scientific notations, the precision filed specifies exactly how many digits to display after the decimal point, even if they are trailing decimal zeros.
std::cout.precision() ist bei mir 6. Im Default-Betrieb also 6 == vor- + nachkommastellen.
Entweder precision anpassen oder auf fixed umstellen, dann ist precision auf die Nachkommastellen angewandt.

Ein Beispiel:

Code: Alles auswählen

#include <iostream>
#include <iomanip>

int main() {
    double num = 12345.67;
    std::cout << std::cout.precision() << std::endl;
    
    std::ios_base::fmtflags default_flags = std::cout.flags(); // flags speichern
    std::cout <<  std::fixed << num << std::endl;
    std::cout.flags(default_flags); // flags resetten
    
    std::cout << num << std::endl;
}
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Das mit dem Creator kann ich nicht beantworten (im vi sehe ich immer beide Ausgaben :wink: ), aber:
kater hat geschrieben:Wiso behandeln? In ein Double passen 15 Stellen oder so und er hat grad mal 7. Was für Informationen sollte man den Computer denn noch geben.
Das ist nicht so einfach... "sehen" tust du die Zahl als Entwickler halt immer als String, welcher (nach bestimmten Regeln) erstellt werden muss. Diese Regeln müssen halt als Entwickler vorgegeben werden (dein '%f'), denn vielfach (eigentlich sogar meistens) möchte man den tatsächlichen Inhalt gar nicht sehen... (Wen interessierts, dass die 6. Nachkommastelle von "double x = 10000000000.0-0.3;" nicht korrekt ist?).

Die gleichen Regeln ergeben die gleichen Ausgaben:

Code: Alles auswählen

  fprintf(stderr, "%f  %.2f %.1f\n",d,d,d);
  qDebug()  << QString::number(d,'f') <<  QString::number(d,'f',2) 
                 << QString::number(d,'f',1) ;

Also: der Operator "<<" von qDebug für double ist etwas grosszügiger (das 'g'-Format, für verbesserte Lesbarkeit). So what?
Das Problem tritt nicht nur bei qDebug auf, sondern die Zahlen werden auch so weitergegeben (z.B. in die Datenbank).
Wer wandelt denn den double in den (SQL-)String? Also mit bindValue(..) des MySQL-Treibers klappts IMHO schon.. und bei einem direkten exec sollte halt der String auch wieder schön mit korrekten Formaten erstellt werden:

Code: Alles auswählen

qDebug()  << "test5" << QString("INSERT INTO ...%1").arg(d,0,'f');

kater hat geschrieben: Weil C das tut was man erwartet.
LOL <Kommentar wieder gelöscht... weil OT>

[EDIT]
bindValue sollte ziemlich sicher klappen, weil in einem Test QVariant(d).toString() den korrekten Wert ("---.67") ergeben hat...
kater
Beiträge: 306
Registriert: 29. Dezember 2009 01:13
Wohnort: Darmstadt

Beitrag von kater »

franzf hat geschrieben:Bevor man sich so arg in Herzinfarkgefahr begibt, sollte man sich kluglesen:
Hehe, ja ich neige zur Übertreibung manchmal. :)

Aber danke für die Erklärung Leute, jetzt bin ich wieder etwas schlauer.
AlexDu
Beiträge: 14
Registriert: 28. Januar 2009 14:08

Beitrag von AlexDu »

Hallo,

danke für die zahlreichen Erläuterungen.

Ich hatte mich darauf verlassen, dass immer alle Stellen, die ein double fassen kann, in den Umwandlungen mitgeführt werden. Ich hatte auch alles brav gerundet. Da gestern das erstemal fünf Stellen vor dem Komma benutzt wurden hatte ich diverse Fehler in Berechnungen und erstmal Panik. Und wenns um Geld geht hört ja bekanntlich der Spass auf ;-).

Ich hab jetzt in alle Datenbankupdates QString::number(meinWert) in QString::number(meinWert,'f',2) geändert und alles ist wieder fein.

Gruß Alex
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

AlexDu hat geschrieben:Und wenns um Geld geht hört ja bekanntlich der Spass auf ;-).
Und wenns ums Geld geht nimmt man sowieso niemals nicht double! Die binäre Darstellung sorgt nur für Probleme, da sie nicht wirklich genau ist.
Nimm unsigned int/long und drücke die Beträge in Cent aus.
AlexDu
Beiträge: 14
Registriert: 28. Januar 2009 14:08

Beitrag von AlexDu »

Nimm unsigned int/long und drücke die Beträge in Cent aus.
Keine schlechte Idee. Wenn das Projekt mal fertig ist, wäre das ein weiterer Punkt fürs refactoring.

Gruß Alex
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Sorry aber wenn man in einer DB einen String anstatt einen real speichert um Fliesskommawerte abzuspeichern ist man selbst schuld...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
AlexDu
Beiträge: 14
Registriert: 28. Januar 2009 14:08

Beitrag von AlexDu »

Sorry aber wenn man in einer DB einen String anstatt einen real speichert um Fliesskommawerte abzuspeichern ist man selbst schuld...
Gespeichert werden sie als Numeric (sqlite). Aber wie schon gesagt, am besten gefällt mir die Variante von franzf. Bearbeiten und speichern als Integer (Cent-Beträge), denn im Prinzip ist ja unsere Währung kein Float.

Gruß Alex
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

[quote="AlexDu"]
Gespeichert werden sie als Numeric (sqlite)/quote]
Und wo ist dann das Problem?
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Antworten