2009-03-30 11 views
6

I eine Funktion getSlope haben, die 4 verdoppelt sich als Parameter verwendet, und gibt ein weiteres Doppel berechnet dieser gegebenen Parameter in der folgenden Art und Weise verwendet:unerwartete Verlust an Präzision bei der Doppel-Dividieren

double QSweep::getSlope(double a, double b, double c, double d){ 
double slope; 
slope=(d-b)/(c-a); 
return slope; 
} 

Das Problem ist, dass, wenn diese Funktion aufrufen mit Argumenten zum Beispiel:

getSlope(2.71156, -1.64161, 2.70413, -1.72219); 

das zurückgegebene Ergebnis ist:

10.8557 

und das ist kein gutes Ergebnis für meine Berechnungen. Ich habe die Steigung mit Mathematica und das Ergebnis für die Steigung für die gleichen Parameter berechnet wird:

10.8452 

oder mit mehr Stellen für Präzision:

10.845222072678331. 

Das Ergebnis wird durch mein Programm zurückgegeben wird, ist nicht gut in meinen weiteren Berechnungen. Darüber hinaus verstehe ich nicht, wie das Programm 10.8557 ab 10.845222072678331 zurückgibt (angenommen, dass dies das ungefähre Ergebnis für die Division ist)? Wie bekomme ich das gute Ergebnis für meine Abteilung?

danken Ihnen im Voraus, madalina


ich das Ergebnis mit der Befehlszeile drucken:

std::cout<<slope<<endl; 

Es kann sein, dass meine Parameter vielleicht nicht gut sind, wie ich sie las aus ein anderes Programm (das ein Diagramm berechnet; nachdem ich diese Parameter aus seinem Diagramm gelesen habe, habe ich sie nur angezeigt, um ihren Wert zu sehen, aber vielleicht haben die angezeigten Vektoren nicht die gleiche interne Genauigkeit für den berechneten Wert. Ich weiß nicht, dass es wirklich seltsam ist Einige numerische al Fehler erscheint ..)

Wenn der Graph, aus dem ich meine Parameter lese, berechnet wird, einige numerische Bibliotheken in C++ geschrieben (mit Vorlagen) verwendet werden. Für diese Berechnung wird kein OpenGL verwendet.

danke, madalina

+0

Überprüfen Sie, wie die Methode in asm kompiliert wurde. Sie können das im Debugger tun, glaube ich (zumindest im Visual Studio). –

+0

Meine Windows-Calc gibt so gute Ergebnisse wie Mathematica: D – klew

+0

Welcher Compiler, welche Plattform? – peterchen

Antwort

5

Könnte es sein, dass Sie DirectX oder OpenGL in Ihrem Projekt verwenden? Wenn dies der Fall ist, können sie die doppelte Genauigkeit ausschalten und Sie werden seltsame Ergebnisse erhalten.

Sie können mit

std::sqrt(x) * std::sqrt(x) 

Das Ergebnis Ihrer Präzision Einstellungen überprüfen hat ziemlich nahe x sein. Ich habe dieses Problem vor langer Zeit getroffen und einen Monat damit verbracht, alle Formeln zu überprüfen. Aber dann habe ich

D3DCREATE_FPU_PRESERVE 
+0

genau, wie machen sie das? –

+0

Es gibt Optionen, wenn Sie direktes 3d initialisieren. Ich erinnere mich nicht an den Namen, aber ich habe einen Monat lang alle Diploma-Formeln überprüft und erst dann eine einfache Überprüfung mit "sqrt (x) * sqrt (x)" vorgenommen und die Genauigkeit war sehr stark, wenn ich die Option nicht abstellte. –

+0

Wenn mit Standard-Win32-Konsole App in VS2008 kompiliert wird, gibt es die richtige Antwort. Ich würde zustimmen und sagen, es ist eine Compiler-Einstellung. –

3

Das Problem hierbei ist, festgestellt, dass (c-a) klein ist, so dass die Rundungsfehler inhärenten in Gleitkommaoperationen ist in diesem Beispiel vergrößert. Eine allgemeine Lösung besteht darin, Ihre Gleichung so zu überarbeiten, dass Sie sich nicht durch eine kleine Zahl teilen. Ich bin mir nicht sicher, wie Sie es hier tun würden.

EDIT:

Neil ist direkt in seinem Kommentar zu dieser Frage, berechnet ich die Antwort in VB mit Double und bekam die gleiche Antwort wie Mathematica.

+0

sehe den Code ich gepostet - das ist nicht das Problem –

5

Der folgende Code:

#include <iostream> 
using namespace std; 

double getSlope(double a, double b, double c, double d){ 
    double slope; 
    slope=(d-b)/(c-a); 
    return slope; 
} 

int main() { 
    double s = getSlope(2.71156, -1.64161, 2.70413, -1.72219); 
    cout << s << endl; 
} 

gibt ein Ergebnis von 10,8452 mit g ++. Wie drucken Sie das Ergebnis in Ihrem Code aus?

+0

Es spielt keine Rolle, wie Sie 10 drucken.845222072678331, wird es nicht rund oder auf 10.8557 abgeschnitten –

1

Besser Drucken Sie die Argumente auch aus. Wenn Sie, wie ich denke, Parameter in Dezimalschreibweise übertragen, verlieren Sie die Genauigkeit für jeden einzelnen von ihnen. Das Problem besteht darin, dass 1/5 eine unendliche Reihe im Binärformat ist, so dass z.B. 0.2 wird .001001001 .... Dezimalstellen werden auch gehackt, wenn eine binäre Gleitkommazahl in eine textuelle Darstellung in Dezimalzahlen umgewandelt wird.

Daneben wählt manchmal der Compiler Geschwindigkeit über die Genauigkeit. Dies sollte ein dokumentierter Compilerwechsel sein.

0

Patrick über (ca) richtig sein scheint die Hauptursache zu sein:

db = -1,72219 - (-1,64161) = -0,08058

ca = 2, 70413 - 2,71156 = -0,00743

S = (db)/(ca) = -0,08058/-0,00743 = 10,845222

Sie mit sechs Ziffern Präzision beginnen , durch die Subtraktion bekommst du eine Reduzierung auf 3 und 4 Ziffern. Meine Vermutung ist, dass Sie zusätzliche Genauigkeit verlieren, weil die Zahl -0,00743 nicht genau in einem Doppel dargestellt werden kann. mit Zwischenvariablen Versuchen Sie, mit einer größeren Präzision, wie folgt aus:

double QSweep::getSlope(double a, double b, double c, double d) 
{ 
    double slope; 
    long double temp1, temp2; 

    temp1 = (d-b); 
    temp2 = (c-a); 
    slope = temp1/temp2; 

    return slope; 
} 
+0

hast du den Code, den ich gepostet habe, angeschaut? –

+0

Sie scheinen Präzision (wie die Zahl dargestellt wird) mit Genauigkeit zu verwechseln (was die Toleranz auf den Werten ist). Ob Sie ein Doppel als 2.70413 oder 2.7041300000 angeben, macht keinen Unterschied in C++ –

+0

@Pete Kirkham: Es ist nicht möglich, einen Wert von z. 0,1 * genau * in einem Double, so dass es in einer Variable mit einem größeren Bereich gespeichert wird, kann zu unterschiedlichen Ergebnissen führen. – Treb

7

ich mit Schwimmer statt Doppel versucht haben und ich bekomme 10,845110 als Ergebnis. Es sieht immer noch besser aus als Madalina.

EDIT:

Ich glaube, ich weiß, warum diese Ergebnisse. Wenn Sie a, b, c und d Parameter von woanders bekommen und Sie es ausdrucken, erhalten Sie abgerundete Werte. Wenn Sie es dann zu Mathemtacia (oder calc;)) setzen, wird es Ihnen ein anderes Ergebnis geben.

Ich habe versucht, ein wenig einen Ihrer Parameter zu ändern. Als ich das tat:

double c = 2.7041304; 

Ich bekomme 10.845806. Ich füge nur 0.0000004 zu c hinzu! Also ich denke, Ihre "Fehler" sind keine Fehler. Drucke a, b, c und d mit besserer Präzision und lege sie dann zu Mathematica.

2

Die Ergebnisse, die Sie erhalten, sind konsistent mit 32-Bit-Arithmetik. Ohne mehr über Ihre Umgebung zu wissen, ist es nicht möglich zu beraten, was zu tun ist.

Angenommen, der angezeigte Code läuft, dh Sie konvertieren nichts in Zeichenfolgen oder Gleitkommazahlen, dann gibt es in C++ keine Korrektur. Es liegt außerhalb des angezeigten Codes und hängt von der Umgebung ab.

Als Patrick McDonald und Treb beide die Genauigkeit Ihrer Eingaben und den Fehler auf a-c brachten, dachte ich, ich würde mir das ansehen. Eine Technik zum Betrachten von Rundungsfehlern ist die Intervallarithmetik, die die oberen und unteren Grenzen, die den Wert repräsentieren, explizit macht (sie sind implizit in Fließkommazahlen enthalten und sind auf die Genauigkeit der Darstellung festgelegt). Indem Sie jeden Wert als obere und untere Grenze behandeln und die Grenzen um den Fehler in der Repräsentation erweitern (ungefähr x * 2^-53 für einen doppelten Wert x), erhalten Sie ein Ergebnis, das die unteren und oberen Grenzen auf der Genauigkeit eines Wertes unter Berücksichtigung von Präzisionsfehlern im schlimmsten Fall.

Wenn Sie beispielsweise einen Wert im Bereich [1.0, 2.0] haben und davon einen Wert im Bereich [0.0, 1.0] subtrahieren, muss das Ergebnis im Bereich [unter (0.0) oben liegen (2.0)] als das minimale Ergebnis ist 1.0-1.0 und das Maximum ist 2.0-0.0. below und above entsprechen Boden und Decke, aber für den nächsten darstellbaren Wert und nicht für Ganzzahlen.

Intervalle, die Worst-Case-Doppel Runden darstellen:

getSlope(
a = [2.7115599999999995262:2.7115600000000004144], 
b = [-1.6416099999999997916:-1.6416100000000002357], 
c = [2.7041299999999997006:2.7041300000000005888], 
d = [-1.7221899999999998876:-1.7221900000000003317]) 
(d-b) = [-0.080580000000000526206:-0.080579999999999665783] 
(c-a) = [-0.0074300000000007129439:-0.0074299999999989383218] 

to double precision [10.845222072677243474:10.845222072679954195] 

So obwohl c-a klein c oder a verglichen wird, ist es immer noch groß im Vergleich zu Doppel Runden, wenn Sie also die denkbar schlechteste Doppel verwendet haben Präzises Runden, dann können Sie darauf vertrauen, dass dieser Wert 12 Zahlen entspricht - 10.8452220727. Sie haben ein paar Zahlen mit doppelter Genauigkeit verloren, aber Sie arbeiten immer noch an mehr als der Bedeutung Ihrer Eingabe.

Aber wenn die Eingänge nur genau auf die Anzahl signifikanten Zahlen waren, dann eher als der doppelte Wert 2,71156 +/- eps ist, dann würde der Eingangsbereich [2.711555,2.711565], so dass Sie das Ergebnis zu erhalten:

getSlope(
a = [2.711555:2.711565], 
b = [-1.641615:-1.641605], 
c = [2.704125:2.704135], 
d = [-1.722195:-1.722185]) 
(d-b) = [-0.08059:-0.08057] 
(c-a) = [-0.00744:-0.00742] 

to specified accuracy [10.82930108:10.86118598] 

das ist eine viel größere Auswahl.

Aber Sie müssten die Genauigkeit der Berechnungen genau verfolgen, und die Rundungsfehler, die dem Gleitkomma inhärent sind, sind in diesem Beispiel nicht signifikant - es ist präzise auf 12 Zahlen mit der Worst-Case-Doppelpräzisionsrundung.

Auf der anderen Seite, wenn Ihre Eingaben nur 6-stellig bekannt sind, ist es eigentlich egal, ob Sie 10.8557 oder 10.8452 erhalten. Beide liegen innerhalb von [10.82930108: 10.86118598].

-1

Während die akademische Diskussion läuft, ist großartig für das Lernen über die Einschränkungen von Programmiersprachen, finden Sie möglicherweise die einfachste Lösung für das Problem ist eine Datenstruktur für arbitrary precision arithmetic.

Dies wird einige Overhead haben, aber Sie sollten etwas mit ziemlich garantierbarer Genauigkeit finden können.

+1

Das Empfehlen von Arithmetik mit beliebiger Genauigkeit, obwohl sie in StackOverflow beliebt ist, ist nicht die beste Antwort auf alle Fragen zu Gleitkommaberechnungen. –

+0

Das ist richtig, aber oft die einfachste praktikable Lösung. Es gibt oft bessere, schnellere und komplexere Methoden, aber selbst die Einfachheit hat in einem Softwareprojekt viel Wert. – IanGilham

Verwandte Themen