title: How does printf %f work on a 32-bit float
body: <p>The <code>%f</code> printf format code is specified as operating on a value of type <code>double</code> [<a href="https://en.cppreference.com/w/cpp/io/c/fprintf" rel="nofollow noreferrer">source</a>]. However, a simple test program demonstrates that it can also be used with values of type <code>float</code>. How does this work?</p>
  
  <p>The equivalent case with integer types (e.g. <code>int</code> and <code>long long int</code>) "works" because on little-endian machines, the low-order bytes of a 32-bit integer happen to overlap the low-order bytes of a 64-bit integer, so as long as the upper bits are 0, you'll get the "right" answer.</p>
  
  <p>But this cannot possibly be the case for <code>float</code> and <code>double</code>, because the floating point formats are not interchangeable like this. You simply cannot print a floating point value as a double without doing the (rather complicated) conversion into the other format. An attempt to do so by type-punning will simply print garbage.</p>
  
  <p>On top of that, <code>printf</code> is variadic. The compiler doesn't necessarily know at compile time what format specifiers will be used, only the types of the arguments. Therefore the only thing I can surmise is that <em>all</em> <code>float</code> values passed to a variadic function would be upgraded to <code>double</code>, unconditionally. But it boggles my mind that I could have been programming in C for so long and not know this.</p>
  
  <p>How is C doing the implicit coercion here?</p>
  
  <p>Source:</p>
  
  <pre><code>#include &lt;stdio.h&gt;
  #include &lt;math.h&gt;
  
  int main() {
    float x[2] = {M_PI, 0.0};
    printf("value of x: %.16e\n", x[0]);
    printf("size of x: %lu\n", sizeof(x[0]));
  
    double *xp = (double *)&amp;x[0];
    printf("value of *xp: %.16e\n", *xp);
    printf("size of *xp: %lu\n", sizeof(*xp));
  
    double y = M_PI;
    printf("value of y: %.16e\n", y);
    printf("size of y: %lu\n", sizeof(y));
  
    int i[2] = {1234, 0};
    printf("value of i: %lld\n", i[0]);
    printf("sizeof of i: %lu\n", sizeof(i[0]));
  
    long long *ip = (long long *)&amp;i[0];
    printf("value of i: %lld\n", *ip);
    printf("sizeof of i: %lu\n", sizeof(*ip));
  
    return 0;
  }
  </code></pre>
  
  <p>Output:</p>
  
  <pre><code>value of x: 3.1415927410125732e+00
  size of x: 4
  value of *xp: 5.3286462644388174e-315
  size of *xp: 8
  value of y: 3.1415926535897931e+00
  size of y: 8
  value of i: 1234
  sizeof of i: 4
  value of i: 1234
  sizeof of i: 8
  </code></pre>
  
  <p>Compile command and version:</p>
  
  <pre><code>$ gcc test_float.c -o test_float
  $ gcc --version
  gcc (Ubuntu 5.5.0-12ubuntu1~16.04) 5.5.0 20171010
  Copyright (C) 2015 Free Software Foundation, Inc.
  This is free software; see the source for copying conditions.  There is NO
  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  </code></pre>