64-bit floating-point-Portierungen
Ich bin der Portierung meiner Anwendung von 32 bit auf 64 bit. Derzeit ist der code kompiliert unter beiden Architekturen, aber die Ergebnisse sind unterschiedlich. Aus verschiedenen Gründen, ich bin mit Schwimmern statt verdoppelt. Ich gehe davon aus, dass es einige implizite upconverting von float zu double geschieht auf einer Maschine und nicht die anderen. Gibt es eine Möglichkeit zu kontrollieren, für diese, oder bestimmte Hinweise, die ich gesucht werden soll?
bearbeitet, um hinzufügen:
32-bit-Plattform
gcc (GCC) 4.1.2 20070925 (Red Hat 4.1.2-33)
Dual-Core AMD Opteron(tm) Processor 2218 HE
64-bit-Plattform
gcc (Ubuntu 4.3.3-5ubuntu4) 4.3.3
Intel(R) Xeon(R) CPU
Anwendung der -mfpmath=387 hilft ein wenig, nach 1 iteration des Algorithmus die Werte sind die gleichen, aber darüber hinaus, dass Sie fallen out of sync wieder.
Ich sollte auch hinzufügen, dass meine Sorge ist nicht, dass die Ergebnisse nicht identisch sind, ist es, dass die Portierung auf eine 64-bit-Plattform entdeckt hat, ein 32-bit-Abhängigkeiten, die ich nicht bewusst war.
- Plattform & compiler sind hier relevant...
- Ja, Sie können erzwingen, dass die FPU. Wie ich bereits in meiner Antwort unten versuchen -mfpmath=387 zu verursachen, es ist nur die Verwendung SSE bei der Verwendung von SSE-Interna.
- stackoverflow.com/questions/982421/...
Du musst angemeldet sein, um einen Kommentar abzugeben.
Es ist keine inhärente Notwendigkeit für floats und doubles, sich anders zu Verhalten zwischen 32-bit und 64-bit-code, aber Häufig tun Sie das. Die Antwort auf Ihre Frage wird sein, Plattform-und compiler-spezifisch, so dass Sie brauchen, um zu sagen, welche Plattform Sie werden die Portierung aus und auf welcher Plattform Sie die Portierung.
Auf intel x86-Plattformen, 32-bit-code, der oft verwendet die x87-co-Prozessor-Befehlssatz und die floating-point-register-stack für maximale Kompatibilität in der Erwägung, dass auf amb64/x86_64-Plattformen, die SSE* Anleitung und xmm* Register und werden oft verwendet. Diese haben unterschiedliche Präzision Merkmale.
Post edit:
Gegeben, Ihre Plattform, möchten Sie vielleicht zu prüfen, versuchen Sie die -mfpmath=387 (Standard für i386 gcc) auf Ihrem x86_64 bauen, um zu sehen, ob dies erklärt die unterschiedlichen Ergebnisse. Sie können auch wollen, schauen Sie sich die Einstellungen für alle -fmath-* compiler-Schalter, um sicherzustellen, dass Sie übereinstimmen, was Sie wollen, in beiden builds.
Dem compiler ist wohl mit SSE-opcodes zu tun, die meisten floating-point-Arithmetik auf die 64-bit-Plattform vorausgesetzt, x86-64, in der Erwägung, dass aus Gründen der Kompatibilität ist es wahrscheinlich die FPU vor, die für viele Ihrer Tätigkeiten.
SSE opcodes bieten viel mehr Register und Konsistenz (Werte bleiben immer 32 bits oder 64 bits in size), während die FPU nutzt 80-bit-Zwischenwerte, wenn möglich. Sie waren also wahrscheinlich profitieren von dieser verbesserten zwischen Präzision vor. (Beachten Sie die zusätzliche Präzision kann die Ursache für inkonsistente Ergebnisse, wie x == y, aber cos(x) != cos (y), je nachdem, wie weit auseinander die Berechnungen auftreten!)
Können Sie versuchen, zu verwenden,- mfpmath=387 für Ihr 64-bit-version, da Sie die Kompilierung mit gcc und sehen, ob Ihre Ergebnisse mit Ihren 32-bit-Ergebnisse zu helfen, die engen diese nach unten.
Wie andere gesagt haben, Sie habe nicht genug Informationen, um genau sagen, was Los ist. Aber in einem Allgemeinen Sinn, es scheint, Sie haben schon das zählen auf eine Art von floating-point-Verhalten, dass Sie sollten nicht zählen.
99 mal von 100 das problem ist, dass man das vergleichen von zwei floats für die Gleichstellung irgendwo.
Wenn das problem ist einfach, dass Sie immer leicht verschiedene Antworten, die Sie brauchen, um zu realisieren, dass weder eine "richtige" -- eine Art von Rundung wird stattfinden, egal was die Architektur, die Sie auf. Es ist eine Frage des Verstehens der signifikanten stellen in Ihren Berechnungen, und sich bewusst ist, dass alle Werte, die Sie kommen mit sind Näherungswerte zu einem gewissen Grad.
Die x87-FPU ist 80-bit interne Register, die Ursache der floating-point-Ergebnisse unterscheiden sich leicht von den anderen FPUs, dass die Verwendung von 64-bit-intern (wie auf x86_64). Erhalten Sie unterschiedliche Ergebnisse zwischen diesen Prozessoren, es sei denn, Sie don ' T Geist unter großen Leistungseinbußen durch Spülen Sie die Dinge aus, um Speicher oder andere "strictfp" tricks.
Siehe auch:
Floating-point-Runden beim abschneiden
Und:
http://docs.sun.com/source/806-3568/ncg_goldberg.html
Auf x64, der SSE2-Befehlssatz verwendet wird, während in 32-bit-apps, die x87-FPU ist oft der Standard.
Letztere speichert intern alle floating-point-Werte in 80-bit-format. Letztere nutzt den einfachen 32-bit-IEEE-floats.
Abgesehen davon, dass ein wichtiger Punkt ist, dass sollten Sie sich nicht darauf verlassen, dass Ihre floating-point Mathematik identisch über Architekturen.
Selbst wenn Sie 32-bit-builds auf beiden Maschinen, es ist immer noch keine Garantie, dass Intel und AMD wird die Ausbeute identische Ergebnisse. Natürlich, wenn einer von Ihnen läuft eine 64-bit-bauen, die Sie nur noch mehr Unsicherheit.
Sich auf die präzisen Ergebnisse einer floating-point-operation kann fast immer ein bug.
Ermöglicht SSE2 auf die 32-bit-version als auch ein guter start wäre, aber wieder, keine Annahmen über floating-point-code. Es ist immer ein Verlust an Präzision, und es ist eine schlechte Idee, zu vermuten, dass dieser Verlust vorhersehbar ist, oder, dass es reproduziert werden kann zwischen den CPU ' s, oder verschiedene builds.
Der gnu compiler hat eine Menge von compiler-Optionen für floating-point-zahlen, die verursachen können, Berechnungen brechen, unter gewissen Umständen. Suchen Sie einfach auf dieser Seite für den Begriff "schwimmen" und du wirst Sie finden.
Es ist wirklich schwer zu kontrollieren eine Menge von diesem Zeug.
Für einen start, die C-standard oft Anrufe für Operationen auf floats zu tun "double-space" umgewandelt und zurück schwimmt.
Intel-Prozessoren haben 80 bit Genauigkeit in den Registern, die Sie verwenden, um viele dieser Operationen, und dann fallen Sie, die auf 64 bit, wenn er gespeichert wird, um Hauptspeicher. Das bedeutet, dass der Wert einer Variablen kann sich ändern, ohne ersichtlichen Grund.
Können Sie Dinge wie GnuMP wenn Sie wirklich, und ich bin sicher, es gibt Bibliotheken, die garantieren gleichbleibende Ergebnisse. Die meisten der Zeit, die Menge von Fehler/jitter erzeugt wird, ist unterhalb der realen Auflösung, die Sie benötigen.
Der wirklich harte Teil zu bekommen, ist, dass beide Ergebnisse korrekt sind. Es ist nicht fair, um die Veränderungen zu charakterisieren als nichts, aber "anders". Vielleicht gibt es eine erhöhte emotionale Bindung zu den älteren Ergebnissen...aber es gibt keinen mathematischen Grund, lieber die 32-bit-Ergebnisse über die 64-bit-Ergebnisse.
Haben Sie sich als ändern Sie für die Verwendung fester Punkt Mathematik für diese Anwendung? Nicht nur ist fixed point math stabil über änderungen von chip, compiler und Bibliotheken, in vielen Fällen ist es schneller als floating point math zu.
Als ein schneller test, verschieben Sie die Binärdatei aus dem 32bit-system die 64bit-system und führen Sie es. Erstellen Sie die app auf dem 64bit-system eine 32bit-binary ist, zu laufen. Das könnte helfen, zu identifizieren, welche änderung(en) sind eigentlich die Herstellung des abweichenden Verhaltens.
Wie bereits erwähnt, anders, sollte kein problem sein, solange Sie sind beide richtig. Im Idealfall sollten Sie haben unit-tests für diese Art von Dinge (Reine Berechnung, in der Regel fällt in die relativ einfach zu testen camp).
Ist es im Grunde unmöglich zu garantieren, die gleichen Ergebnisse in den CPU-und toolchains (ein compiler-flag ändern kann schon eine ganze Menge), und es ist schon sehr schwer, konsequent zu sein. Der Gestaltung robust floating-point-code ist eine harte Aufgabe, aber zum Glück, in vielen Fällen, Präzision ist nicht ein Problem.
Eine wichtige Sache zu beachten ist, dass die C-Sprache, die ursprünglich festgelegt wurde, dass eine Berechnung wie
konvertieren zu b, c und d, um die längsten verfügbaren floating-point-Typ (was geben
double
), fügen Sie zusammen, und dann konvertieren Sie das Ergebnis infloat
. Eine solche Semantik waren einfach für den compiler und hilfreich für den Programmierer, hatte aber leichte Schwierigkeiten: das effizienteste format für speichern zahlen ist nicht das gleiche wie das effizienteste format für Berechnungen durchführen. Auf Maschinen ohne floating-point-hardware, es ist schneller, um Berechnungen durchzuführen, die auf einem Wert, gespeichert als nicht-unbedingt-normalisiert 64-bit-Mantisse und einer separat gespeicherten 15-bit exponent und Vorzeichen, dann zu operieren, die Werte werden als 64-bit -double
die müssen entpackt werden, bevor jede operation und dann normalisiert und umgepackt nach (wenn auch nur, um sofort ausgepackt für die nächste operation). Mit Maschinen halten von Zwischenergebnissen in das längere format verbessert die Geschwindigkeit und Genauigkeit; ANSI-C erlaubt für diesen Typlong double
.Leider ANSI-C nicht um ein Mittel bereitzustellen, durch welche Variablen-argument-Funktionen angeben konnten, ob Sie wollten alle floating-point Werte konvertiert werden, um
long double
alle umgewandeltdouble
oder habenfloat
unddouble
übergebendouble
undlong double
alslong double
. Hatte solch eine Einrichtung existierte, wäre es leicht gewesen, den code, die nicht haben, zu unterscheiden zwischendouble
undlong double
Werte. Leider, das fehlen einer solchen Funktion bedeutet, dass auf Systemen, auf denendouble
undlong double
sind unterschiedliche Typen-code nicht haben, um sich über die Auszeichnung und auf Systeme, in denen Sie nicht es nicht. Dies wiederum bedeutet, dass eine Menge geschriebenen code auf Systemen, auf denen die Typen identisch sind, brechen auf Systeme, in denen Sie nicht; compiler-Hersteller entschieden, der einfachste fix war zu einfach machenlong double
gleichbedeutend mitdouble
und nicht bieten jede Art, die halten konnte intermediate Berechnungen genau.Da fortgeschrittene Berechnungen in einem nichtdarstellbarer Typ ist schlecht, einige Leute entschieden die logische Sache war, haben Berechnungen auf
float
durchgeführt werden als Typfloat
. Es gibt zwar einige hardware-Plattformen kann dies schneller sein als mit Typdouble
es hat oft unerwünschte Folgen für die Genauigkeit. Bedenken Sie:Auf Systemen, auf denen fortgeschrittene Berechnungen werden durchgeführt unter Verwendung
long double
dies ergibt eine gute Genauigkeit. Auf Systemen, auf denen fortgeschrittene Berechnungen durchgeführt werden, wiefloat
diese Ausbeute schrecklich Genauigkeit, selbst wenn a, b, und c sind alle exakt darstellbar ist. Zum Beispiel, wenn a und b sind 16777215.0 f und c ist 4.0 f, der Wert vons
sollte 16777217.0, aber wenn die Summe von a, b und c berechnet sich wiefloat
es wird 1677216.0; dieser wird der Ertrag einer Fläche, die weniger als die Hälfte der korrekte Wert. Wenn a und c wurden 16777215.0 f und b-4.0 f (dieselben zahlen; anderen Reihenfolge), danns
bekommen würde, berechnet als 16777218.0, woraus sich eine Fläche, die 50% zu groß.Wenn Sie Berechnungen durch, bei denen gute Ergebnisse erzielt werden, die auf x86 (viele Compiler für die mit Spannung fördern, um eine 80-bit-Typ, obwohl Sie unhelpfully nicht verfügbar zu machen, um die Programmierer), aber miese Ergebnisse auf x64, ich würde vermuten, Sie haben können, eine Berechnung wie oben, die muss Zwischenschritte durchgeführt, die bei höheren Präzision als die Operanden oder das Ergebnis. Ändern Sie die erste Zeile der oben genannten Methode:
zwingen wird, die intermediate-Berechnungen durchgeführt werden, die in höheren Genauigkeit, anstatt der Durchführung der Berechnungen bei geringer Präzision und die Speicherung der ungenauen Ergebnis in einer höheren Präzision variable.