2009-02-26 6 views
9

ich mich gefragt, ob es einen Weg gibt ein Genauigkeitsproblem zu überwinden, die das Ergebnis meiner Maschine interne Darstellung von Gleitkommazahlen zu sein scheint:Umgang mit Genauigkeitsprobleme in Gleitkommazahlen

Aus Gründen der das Problem Klarheit wird wie folgt zusammengefasst:

// str is "4.600"; atof(str) is 4.5999999999999996 
double mw = atof(str) 

// The variables used in the columns calculation below are: 
// 
//     mw = 4.5999999999999996 
//     p = 0.2 
//     g = 0.2 
//     h = 1 (integer) 

int columns = (int) ((mw - (h * 11 * p))/((h * 11 * p) + g)) + 1; 

Vor einer ganzen Zahl zu Gießen des Ergebnis der Berechnung ist Spalten geben 1,9999999999999996; so nah und doch so weit vom gewünschten Ergebnis von 2.0 entfernt.

Alle Vorschläge sehr willkommen.

+0

diese Frage vor gestellt und beantwortet wurde ... nur danach zu suchen ... –

+0

Lesen Sie auf Numerische Analyse, es ist ein großes Problem in bestimmten Situationen. Vielleicht verwenden Sie alternative (aber langsamere) Mathematikbibliotheken wie BigDecimal usw. – JeeBee

Antwort

4

Eine sehr einfache und effektive Art und Weise eine Gleitkommazahl in eine ganze Zahl abzurunden:

int rounded = (int)(f + 0.5); 

Hinweis: das funktioniert nur, wenn f immer positiv ist. (Danke j zufälliger Hacker)

+0

Angenommen, f ist positiv. –

+0

Ja "Spalten" ist in dieser Anwendung immer positiv. – AndyUK

+0

@j_random_hacker - theoretisch könnte man absoluten Wert verwenden. – Moshe

1

Verwenden Dezimalzahlen: decNumber++

+2

Löst das das 3 * (1/3) Problem? Oder nur das 10 * (1/10) Problem? – MSalters

+1

-1, genau aus dem Grund MSalters gab. Dezimalzahlen sind nützlich, um mit Geld zu arbeiten, nicht weil sie eine höhere Präzision haben, sondern weil Ihre ungenauen Berechnungen mit denen aller anderen identisch sind. Im Übrigen leiden Dezimalzahlen unter genau denselben Problemen. –

+0

Obwohl es einige Bibliotheken gibt, die Brüche speichern. 4.6 wäre 4 + 3/5 in einer von denen. Sie fallen nur auseinander, wenn sie eine Operation erhalten, die unmöglich als Bruchteil zu handhaben ist, wie Multiplikation mit Pi. –

2

du paper lesen kann zu finden, was Sie suchen.

Sie können den absoluten Wert des Ergebnisses erhalten, wie here gesehen:

x = 0.2; 
y = 0.3; 
equal = (Math.abs(x - y) < 0.000001) 
11

Wenn Sie es noch nicht gelesen haben, der Titel this paper wirklich korrekt ist. Bitte lesen Sie es, um mehr über die Grundlagen der Gleitkommaarithmetik auf modernen Computern zu erfahren, einige Fallstricke und Erklärungen, warum sie sich so verhalten, wie sie es tun.

11

Es gibt kein Accuracy-Problem.

Das Ergebnis (1.9999999999999996) unterschied sich vom mathematischen Ergebnis (2) um 1E-16. Das ist ziemlich genau, wenn man Ihre Eingabe "4.600" berücksichtigt.

Sie haben natürlich ein Rundungsproblem. Die Standardrundung in C++ ist abgeschnitten; Sie wollen etwas ähnliches wie Kips Lösung. Details hängen von Ihrer genauen Domain ab, erwarten Sie round(-x)== - round(x)?

+0

+1, um das eigentliche Problem zu erkennen. –

5

Wenn die Genauigkeit wirklich wichtig ist, sollten Sie die Verwendung von Gleitkommazahlen mit doppelter Genauigkeit anstelle von Fließkommazahlen in Betracht ziehen. Obwohl es von Ihrer Frage scheint, dass Sie bereits sind. Sie haben jedoch weiterhin ein Problem mit der Überprüfung bestimmter Werte. Sie müssen Code entlang der Linien von (vorausgesetzt, Sie Ihren Wert gegen Null-Kontrolle):

if (abs(value) < epsilon) 
{ 
    // Do Stuff 
} 

wo „epsilon“ etwas klein, aber nicht den Wert Null.

+1

Ich denke du meinst "abs (berechneter_wert - erwarteter_wert)

+1

In der Tat - aber ich habe erwähnt, dass der Code ein Beispiel für die Überprüfung gegen Null war;) – ChrisF

3

Auf Computern sind Fließkommazahlen niemals exakt.Sie sind immer nur eine enge Annäherung. (1e-16 ist in der Nähe.)

Manchmal gibt es versteckte Bits, die Sie nicht sehen. Manchmal gelten Grundregeln der Algebra nicht mehr: a * b! = B * a. Manchmal zeigt ein Vergleich eines Registers mit der Erinnerung diese feinen Unterschiede auf. Oder mit einem mathematischen Coprozessor gegen eine Fließkomma-Laufzeitbibliothek. (Ich habe lange diese waayyy tooo getan.)

C99 definiert: (Look in math.h)

double round(double x); 
float roundf(float x); 
long double roundl(long double x); 

.

Oder Sie können Ihre eigene Rolle:

template<class TYPE> inline int ROUND(const TYPE & x) 
{ return int((x > 0) ? (x + 0.5) : (x - 0.5)); } 

Für Gleitkomma-Äquivalenz, versuchen:

template<class TYPE> inline TYPE ABS(const TYPE & t) 
{ return t>=0 ? t : - t; } 

template<class TYPE> inline bool FLOAT_EQUIVALENT(
    const TYPE & x, const TYPE & y, const TYPE & epsilon) 
{ return ABS(x-y) < epsilon; }