Menu

#652 Include order affects selection of printf and __mingw_printf

v1.0 (example)
open
nobody
None
5
2017-09-02
2017-08-16
Doug Semler
No

The following prints correctly when compiled with the g++ driver (__USE_MINGW_ANSI_STDIO is defined by the driver, verified by the -E -dM switches). However changing the include order so that math.h is included after stdio.h negates the define somehow and thereafter makes the calls the MSVCRT version of printf (with no support for long double).

NOTE 1: explicitly defining -D__USE_MINGW_ANSI_STDIO=1 during compile returns the correct behavior.
NOTE 2: explicitly defining #define __USE_MINGW_ANSI_STDIO=1 prior to inclusion of math.h in the INCORRECT order does not solve the problem.
NOTE 3: Converting to use the cstdio and cmath headers fixes the issue (include order does not affect behavior).

gcc version 7.1.0 (i686-win32-sjlj-rev1, Built by MinGW-W64 project)
so the v5 runtime I guess? (is there a way to find out the runtime release version out of curiosity???)

#include <math.h>
#include <stdio.h>

int main()
{
   printf("%.40Lf\n", 3.765625e-31L);
   return 0;
}

Discussion

  • niXman

    niXman - 2017-08-16

    gcc version 7.1.0 (i686-win32-sjlj-rev1, Built by MinGW-W64 project)
    so the v5 runtime I guess? (is there a way to find out the runtime release version out of curiosity???)

    yes, v5.x branch

    patches are welcom ;)

     

    Last edit: niXman 2017-08-16
    • Doug Semler

      Doug Semler - 2017-08-16

      Oh, I know a patch would be welcome I put this in here so that it's actually known and tracked lol...

      I've narrowed it down to the fact that unless something from the libstdc++ header directory is included, nothing will unconditionally define __USE_MINGW_ANSI_STDIO as the one that is made in bits/os_defines.h via bits/c++config.h. Until and unless a C++ include is made __USE_MINGW_ANSI_STDIO is made based on the decision in _mingw.h. The only time __cplusplus has any effect in that decision in the mingw header is if _GNU_SOURCE is also defined, meaning that in and of itself, compiling with the C++ driver, while apparantly wanting to use the ANSI implementation, doesn't actually do so all the time.

      Of course, the reason that including the C++ versions in any order makes it work is that they include the appropriate bits...and also the reason that including certain C headers makes it work is that the first directory searched is the libstdc++ directory for the header (which includes the c++config.h and os_config. There are a few headers that will do this if included before stdio.h: math.h stdlib.h fenv.h being a couple of them.

      Compounding the issue is that if stdio.h has already been included, and then one of the c++ headers is included, the __USE_MINGW_ANSI_STDIO is redefined at that point, but the incorrect guarded bits are already brought in leading to the different behavior dependent upon include ordering.

      The question becomes: does this need to be fixed within the CRT (e.g. changing the decision of when to set the __USE_MINGW_ANSI_STDIO or is this actually an issue that needs to be changed in the stdc++ header directory?

       
  • Jim Michaels

    Jim Michaels - 2017-08-22

    ummm, capital L? that isn't in the format spec is it?

     
    • Doug Semler

      Doug Semler - 2017-08-22

      long double is %Lf and has been since at least C99 if not longer. The MS CRT is not compliant (well the one that mingw links against) with respect to the format specifier (many systems still have the MSVCRT.DLL versions that don't have the compliant functionality). In addition, the MS CRT afaik doesn't utilize long double and most of its functions treat long double the same as a double (e.g. long double functions are just mapped to casted version of double). This is why the __USE_MINGW_ANSI_STDIO macro exists in the first place.

       
  • niXman

    niXman - 2017-08-22
    • status: open --> closed-invalid
     
  • niXman

    niXman - 2017-08-29
    • status: closed-invalid --> open
     
  • Zufu Liu

    Zufu Liu - 2017-09-02

    correct with 64bit gcc

    Thread model: posix
    gcc version 7.1.0 (x86_64-posix-seh-rev2, Built by MinGW-W64 project)
    
    D:\work>g++ t3.c
    
    D:\work>a
    0.0000000000000000000000000000003765625000
    
    D:\work>gcc -D__USE_MINGW_ANSI_STDIO t3.c
    
    D:\work>a
    0.0000000000000000000000000000003765625000
    
    Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> 0.0000000000000000000000000000003765625000
    3.765625e-31
    
     
    • Doug Semler

      Doug Semler - 2017-09-02

      No. It is not. Reordering the include files gives different results. Regardless of the correctness, I would expect the standard headers to produce the same results regardless of include order (regardless of what the results are)

      Mainly because the MS VCRT printf cannot handle the long double type as used by gcc. And that's the problem, is the definition choice of __USE_MINGW_ANSI_STDIO when using the g++ driver. I still honestly don't know what the answer is. Is it check the ISO defined compiler flags? The big one is that gcc mingw64 uses a different long double, while the MSVC compiler uses a downcast-to-double, meaning they don't play well together. But there's no way of knowing which one. The libc++ headers define __USE_MINGW_ANSI_STDIO unconditionally if they are ever included.

      What is the best solution? I don't know. I don't know the implications of preferring the ANSI stdio over the microsoft one, but the microsoft CRT that's been used for the past 7 years has been a lot more ANSI complaint and maybe checking the various values of __cplusplus, __STDC__ (and...ummm...__STRICT__? whatever the flag that is set that is the difference between -std=c99 vs -std=g99 etc)

      #include <math.h>
      #include <stdio.h>
      
      int main()
      {
         printf("%.40Lf\n", 3.765625e-31L);
         return 0;
      }
      

      Calls the mingw ANSI long double printf:

      Z:\mingw64>g++ baz.cc && a
      0.0000000000000000000000000000003765625000
      

      However:

      // changing these to <cstdio> and <cmath> in this order for g++ produces
      // the results of the mingw printf.  Also #define _USE_MINGW_ANSI_STDIO
      // prior to stdio.h
      #include <stdio.h>
      #include <math.h>
      
      int main()
      {
         printf("%.40Lf\n", 3.765625e-31L);
         return 0;
      }
      

      Calls the MSVCRT printf and produces

      Z:\mingw64>g++ baz.cc && a
      0.0000000000000000000000000000000000000000
      
      Z:\mingw64>g++ --version
      g++ (x86_64-win32-seh-rev2, Built by MinGW-W64 project) 7.1.0
      
       

Log in to post a comment.

MongoDB Logo MongoDB