vc++ rechnet float statt double

DataNaut

Cadet 2nd Year
Registriert
Nov. 2005
Beiträge
31
Hallo Meisterprogrammierer!
Ich habe eine MFC Anwendung, deren Hauptfenster von CFrameWnd abgeleitet ist.
In der MainFrame Klasse habe ich folgenden Code in einer Methode eingegeben:

double xx1=25, xx2=49;
float yy1=25, yy2=49, tt=yy1/yy2;

cout.precision(16);
cout<< 25.0/49.0<< " " << xx1/xx2<<" "<< tt<< endl;


Das Ergebinss ist:
0.5102040816326531 0.5102040816326531 0.5102040767669678
Die ersten beiden Werte sind die Division der doubles, der letzte die Division der floats.
Der erste Wert wurde wohl schon vom Compiler hergestellt.

Nachdem ich allerdings die Fenstergroesse ändere, und die gleiche Methode aufrufe, ist
das Ergebniss:
0.5102040816326531 0.5102040767669678 0.5102040767669678

Offensichtlich rechnet die Kiste plötzlich mit float (xx1/xx2) obwohl es doch doubles sind.
Ich stehe spachlos vor den Wundern der Computerwissenschaften. Ich kann mir nicht vorstellen, wie das Verändern der Fenstergroesse mit der Maus eine so kleine, einfache Rechnung beeinflussen kann. Weiss jemand Rat?
Hat jemand so etwas Ähnliches schon gesehen?
Vielen Dank für die Mühen,
DataNaut
 
Basierend auf der Tatsache, dass Computer, egal welcher Art und egal welchen Alters, nur Ganzzahlen kennen und Fließkommazahlen durch einen "Trick" dargestellt werden ist das Rechnen mit float oder double grundsätzlich zu vermeiden.

Du erlebst also gerade das Phänomen, das entsteht, wenn man mit Fließkommazahlen rechnet: Falsche und unterschiedliche Ergebnisse.
 
@Boron: Das ändert aber nichts daran, dass dort ein deterministischer Vorgang passiert, dessen Ergebnisse exakt vorhersagbar sind.

Da das Typsystem bereits zur Compilezeit feststeht, rechnet das Programm nicht auf einmal mit float. Also entweder ist das ein Compilerbug (welche Version von VC++ verwendest du?), ein Problem mit dem Format von cout oder du verschweigst uns wichtige Sachen.
 
@Boron: Da bin ich ja mal gespannt, wie man unter grundsätzlicher Vermeidung von double und float auch nur einfache trigonometrische Funktionen berechnen soll. Ganz zu schweigen von der Arithmetik mit komplexen Zahlen, die unser Programm ausgiebig verwendet.


@7H3 N4C3R:
Wir arbeiten mit Visual C++ .Net 2003 und Visual Studio 2003.

Ein Problem mit cout ist es wahrscheinlich nicht, da fprintf das gleiche Ergebniss liefert.

Ich muss gestehen, ja, ich verschweige dir was: Die ca 20 000 anderen Codezeilen. ;)

Nun ist es schwierig zu entscheiden, was davon wichtig ist.
Hier ein überblick:

Zum einen gibts da ne Menge MFC für die GUI.

Dann gibts ein C++ Framework um eine Simulationsbibliothek rum. Das ist alles in simplen C++ geschrieben, sehr klassenorientiert. Ich glaube da gibts keine Arrays über dessen Ende geschrieben wird oder so. Fast alles mit STL implementiert.

Und zu guter letzt auch noch unser Hauptverdächtiger: Virtools.http://www.virtools.com/
Wir benutzen es, um den 3D Inhalt darzustellen und zu manipulieren.
(Bitte jetzt keine Bemerkung wie: Nehmt doch openGL...)

Das Problem zu bescheiben, das eigentlich aufgetreten ist, ginge hier zu weit. Nach einer extensiven debug Sitzung konnten wir das Problem auf diese drei Zeilen reduzieren.
Und der Frage: Wieso rechtent das Programm mit float Präzision, wenn die Variablen doch double sind.
Und:
Wie kann ich herausbekommen, wer mir da wo was zerschiesst?
 
Du kannst natürlich Tools wie Purify oder BoundsChecker auf das Problem werfen und schauen, ob dir irgendjemand im Speicher rumfuhrwerkt. Da, wenn es denn aber passiert, aber Speicher überschrieben wird, der dem Programm eh schon gehört, hätte man mit Tools dieser Art kaum eine Chance.

Dass tatsächlich irgendwo Code überschrieben wird, möchte ich auch bezweifeln, da das Codesegment schreibgeschützt ist.

Wird denn aus dem Code an der Stelle ein .Net-Assembly gemacht oder wird tatsächlich ein "echtes" Kompilat des schuldigen Codes erstellt? Im ersteren Falle könnte man sonst ganz eventuell auf "Laufzeitoptimierungen" der .Net-Umgebung mutmaßen. Dafür kenne ich .Net aber auch leider zu schlecht.

Falls es ein echtes Kompilat ist, wird es spannender. Wird betreffende Stelle geinlinet? Gibt es in dem Code oder in der Klasse Templates? Also dass diese betreffende Quellcode-Stelle mehrfach im Maschinencode vorhanden ist? Dann könnte der Compiler evtl. unterschiedliche Optimierungen anstellen.

Sind alles leider nur sehr vage Vermutungen.

Okay, 20.000 Zeilen anderer Code vereinfachen es dann nicht gerade. :) Aber wenn du so ein funktionierendes Minimalbeispiel hast, ist das schonmal gut.



Einfallen würde mir noch: Anfrage an den Microsoft-Support. Falls ein Bug bekannt ist, würden die das wohl (hoffentlich...) wissen. (ist das VS vollständig "geservicepackt" und gepatcht? rein präventiv)

Und: Memory-Breakpoints auf die Variablen, die sich so "komisch" verhalten können auch helfen. Damit kannst du vielleicht einen Schuldigen erwischen, der irgendwo in den Speicher pfuscht.

Noch eine Idee: Lässt sich das zumindest Teilweise mit einem anderen Compiler übersetzen? Um mal auszuloten, ob der Compiler der Schuldige ist? Ansonsten mal mit und ohne Release-Modus kompilierne. Inlinen komplett abschalten etc. . (bedingt natürlich, dass kein Code in Headern steht)
 
Zuletzt bearbeitet:
Wenn das Problem den Aufwand rechtfertigt, würde ich die betreffenden Codezeilen mal auf Assemblerlebene durchsteppen und die Registerinhalte vergleichen, ob irgendwann falsch referenziert oder doch etwas überschrieben wird.

Da das Problem reproduzierbar ist, bekommt man auf die Weise vielleicht einen Hinweis.

Bzgl. überschreibern könnte auch die STL der Schuldige sein :-). Ich habe in einem grösseren System auch ab und zu seltsame Probleme und habe in dem Zusammenhang auch mal die verwendete STL debuggt. Da kam mir manches auch seltsam vor.

Da der Effekt aber nach verändern der Fenstergösse auftritt und auch MFC Code verwendet wird, wäre der erste Schritt doch einfach mal die Reaktion auf das verändern der Fenstergröße auszukommentieren. Tritt das Problem dann nicht mehr auf, ist der Verursacher doch schon gefunden.

MfG

Arnd
 
Für alle, die sich auch mit diesem Problem beschäftigen, haben wir die Lösung gefunden: Unser Hauptverdächtige Virtools war auch der Übeltäter! Nach Angabe von der Firma Virtools macht die Software einen DirectX call:

IDirect3DDevice9::Reset


"This method does reset the fpu control word to single float precision. " schreibt virtools. Als fix gibt es

_controlfp() defined in <float.h>

In unserer Anwendung haben wir unseren kritischen Aufruf evaluate, der double benötigt so eingeschlossen:

DWORD fp_old, fp_new;

// Save old control mask
fp_old = _controlfp (0, 0);

// Change floating-point precision
_controlfp (_PC_64, _MCW_PC);

// Run Simulation Step
evaluate();

// Restore the old precision mode
_controlfp(fp_old, 0xFFFFFFFF);

Nun funktionierts.

Gruss
DataNaut
 
Bäh, wie krude. :eek: Mein Beileid, wenn man sich mit sowas rumschlagen muss.

Aber vielen Dank für die Auflösung. :)
 
Zurück
Oben