2017-03-27 4 views
0

Ich versuche, über Gleitkomma-Genauigkeit in C zu lernen. Ich stieß auf dieses Problem beim Experimentieren mit Förderung und Herabstufung in math.h Funktionen.zurück mit langen Doppel mit math.h Funktionen

Im folgenden Programm bekomme ich ein -nan für die erste Gleichung mit langen Doppel und ein Ergebnis für die zweite. Ich verstehe nicht warum. Wenn pow(x,y) mit langen Doppel ein -nan zurückgibt, warum gibt (pow(x,y) - z) mit langen Doppel das richtige Ergebnis zurück?

#include <stdio.h> 
#include <math.h> 

/* equation from xkcd: e to the pi minus pi is 19.999099979 */ 
int main(void) 
{ 
    printf("f %#.9f\n", 
      pow(2.71828182846F, 3.14159265359F)); 
    printf("f %#.9f\n", 
      pow(2.71828182846F, 3.14159265359F) - 3.14159265359F); 
    printf("d %#.9lf\n", 
      pow(2.71828182846, 3.14159265359)); 
    printf("d %#.9lf\n", 
      pow(2.71828182846, 3.14159265359) - 3.14159265359); 
    printf("ld %#.9Lf\n", 
      pow(2.71828182846L, 3.14159265359L)); 
    printf("ld %#.9Lf\n", 
      pow(2.71828182846L, 3.14159265359L) - 3.14159265359L); 
    return 0; 
} 

output: 
f 23.140692448 
f 19.999099707 
d 23.140692633 
d 19.999099979 
ld -nan 
ld 19.999099979 

Antwort

3

Es ist nicht definiertes Verhalten, wenn Ihr Formatbezeich die Datentypen der Argumente nicht übereinstimmen passieren Sie printf(a).

Wenn Sie einen anständigen Compiler, würde es Sie davon warnen:

myprog.c:15:12: warning: format '%Lf' expects argument of type 
    'long double', but argument 2 has type 'double' [-Wformat=] 
    printf("ld %#.9Lf\n", 
      ^

Die Lösung ist das Argument, um sicherzustellen, ist des richtigen Typs das Format entweder durch Gießen entsprechen:

printf("ld %#.9Lf\n", (long double)pow(2.71828182846L, 3.14159265359L)); 

oder noch besser:

printf("ld %#.9Lf\n", powl(2.71828182846L, 3.14159265359L)); 

Letzteres wäre bevorzugt, da erstere einen Genauigkeitsverlust mit sich bringen kann.


(a)C11 7.21.6 Formatted input/output functions /9 Siehe für die "undefiniertes Verhalten" Spezifikation:

Wenn ein Argument nicht der richtige Typ für die entsprechende Umwandlung Spezifikation ist, ist das Verhalten nicht definiert.

und /7 desselben Abschnitts für den Typ Erfordernis %Lf:

L Gibt an, dass eine folgende a, A, e, E, f, F, g oder G Konvertierungsspezifizierer gilt für eine lange Doppel Argument.


(b)Mai da die Standardzustände, die jeweils "kleinere" Gleitkommatyps eine Teilmenge der nächsten "größer" Typ ist. Da die Untermenge (im Gegensatz zur richtigen Untermenge) "kleiner oder gleich die Obermenge" bedeutet, ist es möglich, dass doppeltes und langes Doppel exakt dem gleichen zugrundeliegenden Typ entsprechen.

Allerdings ist es sicherer davon auszugehen, dass es ist eine richtige Teilmenge, da Sie nichts wirklich in dieser Annahme verlieren.

+3

Die 'pow' Funktion gibt ein Ergebnis vom Typ' doppeltes ". Sie können das Ergebnis in 'long double' umwandeln, aber für die meisten Zwecke wäre es besser,' powl' aufzurufen, die ein Ergebnis vom Typ 'long double' zurückliefern. –

+0

@paxdiablo: Ich sehe jetzt. Ich habe verstanden, dass pow die langen Doppel die Berechnung reduziert hat, aber vergessen das Ergebnis ist auch ein Doppel. Es arbeitet mit dem Minus-Teil der Gleichung, weil das Pow-Ergebnis dann in dieser Berechnung gefördert wird. Danke für Ihre Antwort. Und jetzt ab, um mehr über Casting zu lernen :) – rsarson

+0

Ist es wert zu erwähnen '', oder ist das Stück schwarze Magie am besten in den Schatten gelassen? –

0

Es ist undefiniertes Verhalten wie paxdiablo darauf hingewiesen. Hier ist, was mein Compiler zeigt als ein Beispiel für die Warnung, die er erwähnt:

gcc main.c -o main 
.main.c:10:24: warning: format specifies type 'long double' but the argument has type 'double' [-Wformat] 
    printf("ld %#.9Lf\n", pow(2.71828182846L, 3.14159265359L)); 
       ~~~~~~  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
       %#.9f 
1 warning generated. 

Und hier ist ein Beispiel, wo NaN nicht angezeigt wird:

$ ./main 
f 23.140692448 
f 19.999099707 
d 23.140692633 
d 19.999099979 
ld -0.000000000 
ld 19.999099979 
Verwandte Themen