How is conversion of float/double to int handled in printf?

前端 未结 7 1823
逝去的感伤
逝去的感伤 2020-11-30 07:56

Consider this program

int main()
{
        float f = 11.22;
        double d = 44.55;
        int i,j;

        i = f;         //cast float to int
        j          


        
相关标签:
7条回答
  • The reason your follow-up code works is because the character constant is promoted to an int before it is pushed onto the stack. So printf pops off 4 bytes for %c and for %d. In fact, character constants are of type int, not type char. C is strange that way.

    0 讨论(0)
  • 2020-11-30 08:36

    printf uses variable length argument lists, which means you need to provide the type information. You're providing the wrong information, so it gets confused. Jack provides the practical solution.

    0 讨论(0)
  • 2020-11-30 08:37

    It's worth noting that printf, being a function with a variable-length argument list, never receives a float; float arguments are "old school" promoted to doubles.

    A recent standard draft introduces the "old school" default promotions first (n1570, 6.5.2.2/6):

    If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.

    Then it discusses variable argument lists (6.5.2.2/7):

    The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

    The consequence for printf is that it is impossible to "print" a genuine float. A float expression is always promoted to double, which is an 8 byte value for IEEE 754 implementations. This promotion occurs on the calling side; printf will already have an 8 byte argument on the stack when its execution starts.

    If we assign 11.22to a double and inspect its contents, with my x86_64-pc-cygwin gcc I see the byte sequence 000000e0a3702640.

    That explains the int value printed by printf: Ints on this target still have 4 bytes, so that only the first four bytes 000000e0 are evaluated, and again in little endian, i.e. as 0xe0000000. This is -536870912 in decimal.

    If we reverse all of the 8 bytes, because the Intel processor stores doubles in little endian, too, we get 402670a3e0000000. We can check the value this byte sequence represents in IEEE format on this web site; it's close to 1.122E1, i.e. 11.22, the expected result.

    0 讨论(0)
  • 2020-11-30 08:39

    There's no such thing as "casting to int in printf". printf does not do and cannot do any casting. Inconsistent format specifier leads to undefined behavior.

    In practice printf simply receives the raw data and reinterprets it as the type implied by the format specifier. If you pass it a double value and specify an int format specifier (like %d), printf will take that double value and blindly reinterpret it an an int. The results will be completely unpredictable (which is why doing this formally causes undefined behavior in C).

    0 讨论(0)
  • 2020-11-30 08:49

    Because you are not using the float format specifier, try with:

    printf("i = %d, j = %d, f = %f, d = %f", i,j,f,d);
    

    Otherwise, if you want 4 ints you have to cast them before passing the argument to printf:

    printf("i = %d, j = %d, f = %d, d = %d", i,j,(int)f,(int)d);
    
    0 讨论(0)
  • 2020-11-30 08:51

    The printf function uses the format specifiers to figure out what to pop off the stack. So when it sees %d, it pops off 4 bytes and interprets them as an int, which is wrong (the binary representation of (float)3.0 is not the same as (int)3).

    You'll need to either use the %f format specifiers or cast the arguments to int. If you're using a new enough version of gcc, then turning on stronger warnings catches this sort of error:

    $ gcc -Wall -Werror test.c
    cc1: warnings being treated as errors
    test.c: In function ‘main’:
    test.c:10: error: implicit declaration of function ‘printf’
    test.c:10: error: incompatible implicit declaration of built-in function ‘printf’
    test.c:10: error: format ‘%d’ expects type ‘int’, but argument 4 has type ‘double’
    test.c:10: error: format ‘%d’ expects type ‘int’, but argument 5 has type ‘double’
    

    Response to the edited part of the question:

    C's integer promotion rules say that all types smaller than int get promoted to int when passed as a vararg. So in your case, the 'd' is getting promoted to an int, then printf is popping off an int and casting to a char. The best reference I could find for this behavior was this blog entry.

    0 讨论(0)
提交回复
热议问题