2012-04-04 1 views
2

Mögliche Duplizieren:
C compiler bug (floating point arithmetic)?Doubles, das sind * genau * gleich 0 sind für (a == b) Rückkehr

ich zwei Doppelzimmer haben, die ich garantieren kann, sind genau gleich 150 Dezimalstellen - dh. Mit dem folgenden Code:

printf("***current line time is %5.150lf\n", current_line->time); 
printf("***time for comparison is %5.150lf\n", (last_stage_four_print_time + FIVE_MINUTES_IN_DAYS)); 

... Rückkehr:

***current line time is 39346.526736111096397507935762405395507812500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 
***time for comparison is 39346.526736111096397507935762405395507812500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 

FIVE_MINUTES_IN_DAYS ist #defined und current_line->time und last_stage_four_print_time sind beide Doppelzimmer.

Mein Problem ist, dass die nächste Zeile meines Debugging-Code:

printf("if condition is %d\n", (current_line->time >= (last_stage_four_print_time + FIVE_MINUTES_IN_DAYS))); 

gibt die folgende:

if condition is 0 

Kann mir jemand sagen, was hier los? Ich bin mir der nichtdezimalen/ungenauen Natur von floats und doubles bewusst, aber diese unterliegen überhaupt keinem Fehler (die ursprünglichen Zahlen wurden alle mit sscanf oder # definiert gelesen und sind alle auf 10 Dezimalstellen spezifiziert).

EDIT: Mein Fehler war die Annahme, dass printf -ing das Doppel genau sie im Speicher dargestellt, die falsch war, weil ein Wert on-the-fly berechnet wird. (last_stage_four_print_time + FIVE_MINUTES_IN_DAYS) als threshold_time deklarieren und stattdessen das Problem behoben. Ich werde dafür sorgen, dass ein Epsilon für meine Vergleiche verwendet wird - ich wusste, dass dies der richtige Weg war. Ich war nur verwirrt darüber, warum diese Werte, die ich (fälschlicherweise) für identisch hielt, anscheinend ungleich waren.

+0

Was ist die '# define' für' FIVE_MINUTES_IN_DAYS'? Ist es ein 'langes Doppel'? Es gibt eine Menge, die du uns nicht zeigst. – Potatoswatter

+0

Wow, 7 Antworten in etwa 5 Minuten: D FIVE_MINUTES_IN_DAYS ist definiert als '#define FIVE_MINUTES_IN_DAYS 0.0034722222' –

+0

Speichern Sie Ihre' a + b' in einem Float. Verwenden Sie einen nicht signierten Char-Zeiger und geben Sie die Adresse des Floats ein und prüfen Sie, ob die Bitmuster der beiden Floats wirklich übereinstimmen. Wenn sie es tun, dann ist da etwas faul. Else die Antworten hier sind die Möglichkeiten zu gehen –

Antwort

4

Floats tun sicherlich nicht genau zu 150 signifikanten Stellen, so dass ich bin nicht sicher, welche Schlussfolgerung aus dem ‚visuellen‘ -Vergleich (falls vorhanden) gezogen werden.

Auf der anderen Seite sind die Werte offensichtlich nicht bitidentisch (und wie könnten sie sein, da einer von ihnen vor Ort mit Zusatz berechnet wird?). Es ist also nicht wirklich klar, warum das Verhalten, das Sie sehen, unerwartet ist.

Do not jemals vergleichen Schwimmer wie das, tun Sie einfach den Standard Vergleich von Unterschied vs Epsilon.

+0

Ich weiß, dass das 150-stellige Bit ungenau war, ich benutzte das, um die Werte im Speicher zu überprüfen. Sie haben genau richtig mit der Berechnung vor Ort - eine Variable 'threshold_value' zu ​​deklarieren als' (last_stage_four_print_time + FIVE_MINUTES_IN_DAYS) 'das Problem behoben. –

3

Sie sollten immer einen EPSILON-Wert für den Vergleich von Floats und Doubles verwenden, um auf Gleichheit zu prüfen. Auch wenn die interne Darstellung so aussieht, als ob diese Typen von Zahlen in binärer Form dargestellt werden, ist die Übereinstimmung nicht garantiert.

Sie können so etwas wie

#define EPSILON 0.00001 
... 
if (fabs(a - b) <= EPSILON) return 1; // they are equal 
return 0; 
2

Jesus hat recht, wie man das löst.

Warum? In einem Fall lesen Sie einen konstanten Wert, im anderen Fall führen Sie einen Additionsvorgang durch. Auch wenn die Druckausgabe genau gleich ist, kann die Binärdarstellung geringfügig abweichen.

Versuchen Sie, den Speicher zu prüfen, der die zwei Doppelgänger unterstützt, und sehen Sie, ob irgendwelche Bits unterschiedlich sind (es wird Unterschiede geben).

Für eine umfassende Behandlung, empfehle ich

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

+0

'Warum ... in einem Fall lesen Sie in einem konstanten Wert, in dem anderen Fall führen Sie eine Addition Operation. - genagelt, siehe die Bearbeitung :) –

1

Im Allgemeinen nicht == vergleichen Schwimmern oder Doppel verwenden sollten. Sie sollten stattdessen überprüfen, dass der Unterschied kleiner als eine kleine Zahl ist.

double second_number = last_stage_four_print_time + FIVE_MINUTES_IN_DAYS; 
if (fabs(current_line->time - second_number) < 0.001 || current_line->time > second_number){ 
    // your comparison code 
} 
1

Erstens haben Doppel nur 15-16 Dezimalstellen (log_2 von 52 Bit Matissa).

Zweitens, wenn Sie vergleichen möchten, verwenden Sie das bereits erwähnte Epsilon.

Drittens, zum Debuggen, drucken Sie den Hex-Wert.

4

Lesen Sie über Gleitkommadarstellung (besonders http://en.wikipedia.org/wiki/IEEE_754-2008). Versuchen Sie, den tatsächlichen Inhalt der Bytes, die die Doubles enthalten, als hexadezimal auszugeben, und sie werden Bit für Bit nicht übereinstimmen. Der richtige Vergleich für Floats ist in Knuth (Seminumerical Algorithmen). Einfach (ersetzen Bool mit int und float mit Doppel, true mit 1):

bool almostEqual(float x, float y, float epsilon) 
{ 
    if (x == 0.0 && y == 0.0) { 
     return true; 
    } 

    if (fabs(x) > fabs(y)) { 
     return fabs((x - y)/x) < epsilon; 
    } else { 
     return fabs((x - y)/y) < epsilon; 
    } 
} 
+1

Der Weg zum Drucken der Byte Inhalt: void main() { doppelt a = 3,0; Zeichen * b = (Zeichen *) & a; int ii = 0; für (ii = 0; ii subhacom

+0

+1 und die Regel ist: _ "Nie, niemals floats direkt vergleichen, es sei denn es ist eine Null" _ ;-) – Arsen7

+0

Bitte fügen Sie diese beiden Zeilen als erste einchecken in Ihrer Methode, um Ihren Code NaN bewusst: (x! = x) {zurück (y! = y)? wahr falsch; } if (y! = Y) {return false; } – Totonga