2016-08-31 1 views
5

Ich lese diese in einem populären Buch über Computergrafik,Skipping „Test für Null“ checks in IEEE 754

Es gibt viele numerische Berechnungen, die viel einfacher geworden, wenn der Programmierer die Vorteile der IEEE-Regeln erfolgt. Betrachten wir zum Beispiel den Ausdruck:

a = 1/(1/b + 1/c)

Solche Ausdrücke mit Widerständen und Linsen auftreten. Wenn Dividieren durch Null zu einem Programmabsturz führte (wie es in vielen Systemen vor dem Gleitkomma-IEEE der Fall war), dann wären zwei if-Anweisungen erforderlich, um nach kleinen oder Nullwerten von b oder c zu suchen. Bei einem IEEE-Fließkommawert erhalten wir, wenn b oder c Null ist, nach Bedarf einen Nullwert für a.

Aber was ist mit dem Fall, wo b=+0 und c=-0? Dann a=1/inf-inf=nan. Ist das Buch falsch über diese Optimierungen oder habe ich etwas falsch verstanden? Es scheint, als ob wir immer noch mindestens einen Scheck für die Zeichen von b & c benötigen, anstatt überhaupt keine Schecks.

Bearbeiten Ein Vorschlag in den Kommentaren war nur eine Nachprüfung für NaN. Ist das der idiomatische Weg, diesen Zahlentyp "auszunutzen"?

bool is_nan(float x) { return x != x; } 

float optic_thing(float b, float c) { 
    float result = 1.0f/(1.0f/b + 1.0f/c); 
    if (is_nan(result)) result = 0; 
    return result; 
} 
+1

Wäre nicht eine einfache Nachprüfung für NaN ausreichend? – bipll

+1

Wahrscheinlich vermisse ich etwas, aber es ist unklar, warum die obige Aussage wahr wäre. Wenn entweder b oder c Null ist, wird dieser Ausdruck 1/unendlich, was nicht Null ist. –

+0

Sie müssten * drei * Checks von 0 für Ihren Ausdruck benötigen, wenn 'b' oder' c' negativ sein können. Denn '1/b + 1/c' wird' 0' wenn 'b == -c' –

Antwort

2

Aber was ist mit dem Fall, in dem b=+0 und c=-0?

Convert -0.0-+0.0 ohne durch 0.0 Zugabe Verzweigung.

int main(void) { 
    double b = +0.0; 
    double c = -0.0; 
    printf("%e\n", b); 
    printf("%e\n", c); 
    printf("%e\n", 1.0/(1.0/b + 1.0/c)); 
    b += 0.0; 
    c += 0.0; 
    printf("%e\n", 1.0/(1.0/b + 1.0/c)); 
    return 0; 
} 

Ausgabe

0.000000e+00 
-0.000000e+00 
nan 
0.000000e+00 

[Bearbeiten] Auf der anderen Seite, werden alle Werte in der Nähe von 0.0, aber nicht 0.0, sind wahrscheinlich numerische Artefakte und nicht genau Widerstand Werte. Das oben genannte hat immer noch Probleme mit winzigen Wertpaare wie b = DBL_TRUE_MIN; c = -b; Das Problem ist 1.0/tiny ->Infinity und +Infinity + -Infinity ->NAN. Man könnte entweder einen breiteren Gleitkommawert für die Quotientenberechnung verwenden oder die Operanden eingrenzen.

b += 0.0; 
    c += 0.0; 
    printf("%Le\n", 1.0/(1.0L/b + 1.0L/c)); 

    // Lose some precision, but we are talking about real resistors/lenses here. 
    b = (float) b + 0.0f; 
    c = (float) c + 0.0f; 
    printf("%e\n", 1.0/(1.0/b + 1.0/c)); 
+1

+ Null hinzufügen ist in der Tat die kanonische Art "aufzuräumen" unerwünschte Instanzen von -zero. Es kann auch mit der Multiplikation kombiniert werden, falls erforderlich, z.B. 'fma (a, b, +0.0)'. Natürlich setzt dies alles voraus, dass man mit einem Compiler (und Compiler-Switches) arbeitet, der in erster Linie das richtige IEEE-754-Verhalten respektiert (d. H. Die Additionen mit Null nicht optimiert). – njuffa

+0

@njuffa True über "Respekt richtig IEEE-754". Glücklicherweise ist OPs Post mit IEEE 754 getaggt und betitelt. BTW: Sie können genießen, dass [+0.0 und -0.0 verschiedene arithmetische Ergebnisse ergeben] (http://stackoverflow.com/q/25332133/2410359), was etwas das Gegenteil dessen ist, was OP ist versuchen zu tun. – chux

+0

Ich bin mir der Frage und Ihrer Antwort (jetzt Community-Wiki) offensichtlich bewusst, weil ich sehe, dass ich die Antwort aufgewertet habe. Da ich die Antwort nicht kommentiert oder bearbeitet habe, gehe ich davon aus, dass ich nicht an ein Beispiel denken konnte, das nicht schon erwähnt wurde. – njuffa

Verwandte Themen