我试图了解C中的浮点精度。我在math.h函数中尝试升级和降级时遇到了这个问题。
在下面的程序中,我得到了一个使用long double的第一个等式的-nan和第二个得到的结果。我不懂为什么。如果带有长双精度的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
答案 0 :(得分:3)
如果格式说明符与传递给printf
(a)的参数的数据类型不匹配,则为未定义的行为。
如果您使用的是合适的编译器,它会警告您:
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
了解&#34;未定义的行为&#34;规格:
同一部分的如果任何参数不是相应转换规范的正确类型,则行为未定义。
和/7
表示%Lf
的类型要求:
L
指定以下a
,A
,e
,E
,f
,{ {1}},F
或g
转换说明符适用于 long double 参数。
(b) 5月, 因为标准规定了每个&#34;较小的&#34;浮点类型是下一个&#34;更大&#34;的一个子集。类型。由于子集(与正确的子集相对)意味着&#34;较小或等于超集&#34;,因此double和long double可能是完全相同的底层类型。
然而,假设它 是一个合适的子集是更安全的,因为你在这个假设中并没有真正失去任何东西。
答案 1 :(得分:0)
这是paxdiablo指出的未定义行为。以下是我的编译器显示的警告示例:
$ ./main
f 23.140692448
f 19.999099707
d 23.140692633
d 19.999099979
ld -0.000000000
ld 19.999099979
这是一个NaN没有出现的例子:
{{1}}