开发者

Long Double in C

I've been reading the C Primer Plus book and got to this example

#include <stdio.h>
int main(void)
{
    float aboat = 32000.0;
    double abet = 2.14e9;
    long double dip = 5.32e-5;

    printf("%f can be written %e\n", aboat, aboat);
    printf("%f can be written %e\n", abet, abet);
    printf("%f can be written %e\n", dip, dip);

    return 0;
}

After I ran this on my macbook I was quite shocked at the output:

32000.000000 can be written 3.200000e+04
2140000000.000000 can be written 2.140000e+09
2140000000.000000 can be written 2.140000e+09

So I looked round and found out that the correct format to display long double is to use %Lf. However I still can't understand why开发者_StackOverflow社区 I got the double abet value instead of what I got when I ran it on Cygwin, Ubuntu and iDeneb which is roughly

-1950228512509697486020297654959439872418023994430148306244153100897726713609
013030397828640261329800797420159101801613476402327600937901161313172717568.0
00000 can be written 2.725000e+02

Any ideas?


Try looking at the varargs calling convention on OSX, that might explain it.

I'm guessing the compiler passes the first long double parameter on the stack (or in an FPU register), and the first double parameter in CPU registers (or on the stack). Either way, they're passed in different places. So when the third call is made, the value from the second call is still lying around (and the callee picks it up). But that is just a guess.


The C Standard Library's printf() function is an example of a variadic function, that is one which can take different numbers of arguments. The way in which the C language implements this, the called function must know what type of arguments were passed in which order so that it can interpret them correctly. This is why you pass a format string, so that printf() can make correctly sense of the data it has to print.

If a variadic function incorrectly interprets the arguments passed to it, the C standard specifies that the behaviour is undefined, that is anything can happen (C89 standard para 4.8.1.2). In your case, where you are passing in non-matching formats and values to printf(), that is what is happening. However, if you have a decent compiler and your warning levels turned up to something sensible, you should be warned about this at compile-time. For example, on Cygwin, I get:

$ make go
cc -g -W -Wall -Wwrite-strings -ansi -pedantic    go.c   -o go
go.c: In function `main':
go.c:10: warning: double format, long double arg (arg 2)
go.c:10: warning: double format, long double arg (arg 3)
go.c:10: warning: double format, long double arg (arg 2)
go.c:10: warning: double format, long double arg (arg 3)
$ 

As to why you get specifically what you are seeing, this will depend on the particular implementation. In practice, what is likely to be happening is that your particular implementation of printf() is interpreting the first half of your long double as a double and printing the value which corresponds to that particular bit pattern. However, as the standard states, it could do whatever it likes.


Use specifier as %LF for the long double instead of %lf or %f. %LF always has a different meaning than %lf.

#include <stdio.h>
int main(void)
{
    float aboat = 32000.0;
    double abet = 2.14e9;
    long double  dip = 5.32e-5L;

    printf("%f can be written %e\n", aboat, aboat);
    printf("%f can be written %e\n", abet, abet);
    printf("%LF can be written %LE\n", dip, dip);

    return 0;
}

Output:

Long Double in C

32000.000000 can be written 3.200000e+04
2140000000.000000 can be written 2.140000e+09
0.000053 can be written 5.320000E-05


Maybe the 64-bit ABI is different in such a way that printf looks for %f arguments in a completely different place than %LF arguments.

Try looking at the assembly output (gcc -S) to see if this is true.


I'm reading C Primer Plus, like you I noticed the same thing. See how I changed the format specifiers for the third printf statement.

#include <stdio.h>
#include <inttypes.h>

int main(void){

    float aboat = 320000.0;
    double abet = 2.214e9;
    long double dip = 5.32e-5;

    printf("%f can be written %e\n", aboat, aboat);
    printf("%f can be written %e\n", abet, abet);
    printf("%Lf can be written %Le\n", dip, dip);

    return 0;
}

Results after changing the format specifiers

320000.000000 can be written 3.200000e+05
2214000000.000000 can be written 2.214000e+09
0.000053 can be written 5.320000e-05
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜