2017-03-27 65 views
0

我正在嘗試使用C語言學習浮點精度。在math.h函數中嘗試升級和降級時遇到此問題。nan用math.h函數返回long long double函數

在下面的程序中,我得到了第一個使用長雙打和第二個結果的第一個等式。我不懂爲什麼。如果帶長雙打的pow(x,y)返回一個-nan,爲什麼(pow(x,y) - z)有長雙打會返回正確的結果?

#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 

回答

3

這是不確定的行爲,如果你的格式說明不匹配的參數的數據類型傳遞給printf(一)

如果你使用的是體面的編譯器,它會提醒你的是:

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

的解決方案是確保參數是正確的類型或者通過鑄造相匹配的格式

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

,或者更好的:

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

後者將是首選,因爲前者可能會損失精確度(b)


的(a)參見C11 7.21.6 Formatted input/output functions /9爲「未定義的行爲」規範:

如果任何參數是不正確的類型的相應的轉換說明書中,該行爲是未定義。

和相同節的%Lf類型要求的/7

L用於指定後續aAeEfFg,或G轉換符適用於long double的說法。


(b)中月,因爲標準規定每個 「較小」 浮點類型是下一個 「大」 類型的子集。由於子集(與正確的子集相反)意味着「更小的或等於超集」,所以double和long double可能是完全相同的基礎類型。

但是,假設它是是一個合適的子集是安全的,因爲您在該假設中並沒有真正失去任何東西。

+3

的'pow'函數返回類型的'結果double'。您可以將結果轉換爲「long double」,但對於大多數用途,最好調用'powl',它返回'long double'類型的結果。 –

+0

@paxdiablo:我明白了。我理解戰俘降級長雙打計算,但忘記了返回的結果也是一個雙。它與等式的負數部分一起工作,因爲在該計算中pow函數的結果會被提升。感謝回覆。現在關閉瞭解更多關於鑄造:) – rsarson

+0

值得一提的是'',還是那塊黑魔法最好留在陰影裏? –

0

paxdiablo指出這是未定義的行爲。下面是我的編譯器顯示,因爲他提到了警告的例子:

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. 

下面是一個例子,其中的NaN沒有顯示出來:

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