If you compile TDM-GCC 5.1.0 with c99
as shown in gcc-std=c99-pedantic test.c
, the output of %lf
will be 0.0000
.
Source code of interest:
#include<stdio.h>
int main(void){
double v=3.1415926;
printf("%f\n", v);
printf("%lf\n",v);
}
Run Results (compiled with c99 option):
3.141593
0.000000
On the other hand, compiling with no option specified (gcc test.c
) results as expected.
Run Results (compiled without c99 option):
3.141593
3.141593
The use of %lf
in printf
should be appropriate for C99, which is a strange result.
In the previous version of GCC (MinGwgcc 3.2), %lf
was available when using the c99
option, so I think it is a unique bug in this GCC, but I am looking for a way to avoid it for the time being (until bug fixes).
C99
There is no problem with compiling without the option, so I think we can avoid it in some way.
MinGW based on TDM-GCC has two types of stdio: MinGW stdio and MSVCRT stdio. It can be accessed under the names _mingw_printf()
and _msvcrt_printf()
.
#include<stdio.h>
int main(void){
double v=3.1415926;
printf("%f\n", v);
printf("printf(): %lf\n", v);
__mingw_printf("_mingw_printf(): %lf\n", v);
__msvcrt_printf("_msvcrt_printf(): %lf\n", v);
}
MSVCRT stdio is MSVCRT.dll
, but this is from Visual C++ 6.0 released in 1998, so it has nothing to do with C99 or C11.Format Specification Fields: printf and wprintf Functions has a document with %zu
Now, MinGW stdio and MSVCRT stdio switch operations with the value __USE_MINGW_ANSI_STDIO
as described below in <stdio.h>
.
#if_USE_MINGW_ANSI_STDIO
/*
* User has expressed a preference for C99 conformance...
*/
(omitted)
__mingw_stdio_redirect__
int printf(const char*__format,...)
{
register int__retval;
__buildtin_va_list__local_argv;_buildtin_va_start(_local_argv,__format);
__retval=_mingw_vprintf(_format,__local_argv);
__buildin_va_end(_local_argv);
return__retval;
}
(omitted)
# else
/*
* Default configuration: Simply direct all calls to MSVCRT...
*/
_CRTIMP int__cdecl_MINGW_NOTHROW fprintf(FILE*, const char*,...);
_CRTIMP int__cdecl_MINGW_NOTHROW printf(const char*,...);
_CRTIMP int__cdecl_MINGW_NOTHROW sprintf(char*, const char*,...);
_CRTIMP int__cdecl_MINGW_NOTHROW vfprintf (FILE*, const char*, __VALIST);
_CRTIMP int__cdecl_MINGW_NOTHROW vprintf(const char*,__VALIST);
_CRTIMP int__cdecl_MINGW_NOTHROW vsprintf(char*, const char*, __VALIST);
#endif
where __USE_MINGW_ANSI_STDIO
is set to <_mingw.h>
.
/*Activation of MinGW specific extended features:
*/
# ifndef__USE_MINGW_ANSI_STDIO
/*
* If user didn't specify it explicitly...
*/
# if defined__STRICT_ANSI__||defined_ISOC99_SOURCE\
|| defined_POSIX_SOURCE||defined_POSIX_C_SOURCE\
|| defined_XOPEN_SOURCE||defined_XOPEN_SOURCE_EXTENDED\
|| defined_GNU_SOURCE||defined_BSD_SOURCE\
|| defined_SVID_SOURCE
/*
* but where any of these source code qualifiers are specified,
* then Assume ANSI I/O standards are preferred over Microsoft's...
*/
# define__USE_MINGW_ANSI_STDIO1
# else
/*
* otherwise use whatver_MINGW_FEATURES_specifications...
*/
# define__USE_MINGW_ANSI_STDIO(__MINGW_FEATURES_&__MINGW_ANSI_STDIO_)
# endif
#endif
Compilation options -std=c99
and -ansi
also define __STRICT_ANSI_
in conjunction with defining __USE_MINGW_ANSI_STDIO
.
As a result, MinGW stdio and MSVCRT stdio switch.
If you understand the principles, it's clear.
-std=gnu99
(not -std=c99
or -ansi
)-D_USE_MINGW_ANSI_STDIO=0
_msvcrt_printf()
instead of printf()
That's about it.
Verified the TDM-GCC source code mingwrt-3.21-mingw32-src.tar.xz
where printf()
ends up in mingwex/stdio/format.c
as defined in mingwex/stdio/format()
.
case 'l':
/*
* Interpret the argument as explicitly of a
* `long' or `long' data type.
*/
if(*fmt=='l')
{
/* Modifier is `ll'; data type is `long' sized...
* Skip the second `l', and set length recordingly.
*/
++fmt;
length = PFORMAT_LENGTH_LLONG;
}
else
/* Modifier is `l'; data type is `long' sized...
*/
length = PFORMAT_LENGTH_LONG;
# ifndef_WIN32
/*
* Microsoft's MSVCRT implementation uses `l'
* as a modifier for `long double'; if we don't want
* to support that, we end this case here...
*/
state = PFORMAT_END;
break;
/* elsewise, we simply fall through...
*/
# endif
case 'L':
/*
* Identify the approve argument as a `long double',
* when associated with `%a', `%A', `%e', `%E', `%f', `%F',
* `%g' or `%G' format specifications.
*/
stream.flags | = PFORMAT_LDOUBLE;
state = PFORMAT_END;
break;
In environments where _WIN32
is not defined, the C99 equivalent l
flag is ignored, but in environments where _WIN32
is defined, falling through to the L
flag to match MSVCRT operation is considered long double
.
Regarding MSVCRT, historically in the 16-bit era, float
32bit, double
64bit, and long double
80bit.Therefore, %lf
was used when printf()
the long double
.I changed it to long double
64bit when I turned Windows 95/32bit.As a result, whether the %lf
in printf()
represents long double
or double
represents double
, the path is 64 bits and has no effect on printf()
.
However, the MinGW has its own long double
set to 96 bits (originally, it should be 64 bits for Visual C++).This can only be described as a specification bug. And as mentioned above, printf()
drags the behavior of Visual C++ in the 16-bit era and considers %lf
as long double
.
To address this discrepancy, printf("%lf\n", (long double)v);
must be explicitly cast and correctly argumentated.
Another fundamental solution is to use the gcc compilation option -mlong-double-64
.However, you will need to rebuild all runtime, including printf()
.Or is it faster to rebuild from gcc?
<_mingw.h>
contains the following comments:
/*These are defined by the user (or the compiler)
to specify how identifiers are imported from a DLL.
__DECLSPEC_SUPPORTED Defined if dlimport attribute is supported.
__MINGW_IMPORT The attribute definition to specify imported
variables/functions.
_CRTIMP Asabove. For MS compatibility.
__MINGW32_VERSION Runtime version.
__MINGW32_MAJOR_VERSION Runtime major version.
__MINGW32_MINOR_VERSION Runtime minor version.
__MINGW32_BUILD_DATE Runtime build date.
Macros to enable MinGW features which generate from standard MSVC
compatible behaviour; these may be specified directly in user code,
activated explicitly, (e.g. by specifying_POSIX_C_SOURCE or Such),
or by inclusion in __MINGW_FEATURES__:
__USE_MINGW_ANSI_STDIO Select a more ANSI C99 compatible
implementation of printf() and friends.
It says These are defined by the user or the may be specified directly in user code, so you can specify it.
In the first place, this behavior relies heavily on the internal implementation of MinGW stdio and MSVCRT stdio switching choices, so you should be very careful when you specify it, and if you do it carefully, you should be hesitant.
For example, what happens if you use _POSIX_C_SOURCE
, _XOPEN_SOURCE
(or _GNU_SOURCE
) as pointed out in the previous email?
Please review this response and code carefully.#if_USE_MINGW_ANSI_STDIO
will not be achieved by #define
either way.
I want (TDM-GCC) to work on C99
However, as I commented on the return value of snprintf, standards, specifications, and implementation are different issues.The GCC may be C99 compliant within the compiler's responsibility, but the MinGW and Visual C++ 6.0 that TDM-GCC uses in its running environment should not be C99 compliant.In general, TDM-GCC is not C99 compliant.
If you want to use C99, you should choose a C99 compliant environment.
When -std=c99
is specified, is it treated the same as "%Lf"
?
printf()
is a variable length argument and float
is extended to double
, so "%lf"
was a mistake in the past.
Although C99 now allows "%lf"
, you don't have to use "%lf"
.
#include<stdio.h>
int main (void)
{
double v=3.1415926;
printf("sizeof(double)=%u\n", (unsigned)sizeof(double)));
printf("sizeof(long double)=%u\n", (unsigned)sizeof(long double)));
printf("%%f-double:%f\n", v);
printf("%%lf-double:%lf\n", v);
printf("%%Lf-double:%Lf\n",v);
printf("%%f-(long double):%f\n", (long double)v);
printf("%%lf-(long double):%lf\n", (long double)v);
printf("%%Lf-(long double):%Lf\n", (long double)v);
}
Compilation results:
d:\tmp>gcc-Wall-pedantic-std=c99 double.c
Results:
d:\tmp>a.exe
size of (double) = 8
size of (long double) = 12
%f-double —3.141593
%lf-double: 0.000000
%Lf-double: 0.000000
%f-(long double): -887936461558575477275137080339417618448384.000000
%lf - (long double): 3.141593
%Lf-(long double): 3.141593
Compilation results:
d:\tmp>gcc-Wall-pedantic double.c
double.c: In function 'main':
double.c:10:12:warning:unknown conversion type character'L'in format [-Wformat=]
printf("%%Lf-double:%Lf\n",v);
^
double.c:10:12:warning:too many arguments for format [-Wformat-extra-args]
double.c:11:12:warning:format '%f' expectations argument of type 'double', but argument2 has type 'long double' [-Wformat=]
printf("%%f-(long double):%f\n", (long double)v);
^
double.c:12:12:warning:format '%lf' expectations argument of type 'double', but argument 2 has type 'long double' [-Wformat=]
printf("%%lf-(long double):%lf\n", (long double)v);
^
double.c:13:12:warning:unknown conversion type character'L'in format [-Wformat=]
printf("%%Lf-(long double):%Lf\n", (long double)v);
^
double.c:13:12:warning:too many arguments for format [-Wformat-extra-args]
Results:
d:\tmp>a.exe
size of (double) = 8
size of (long double) = 12
%f-double —3.141593
%lf-double —3.141593
%Lf-double: 3.141593
%f-(long double): -887936461558575470000000000000000000000.0000000000000000000000000000000000000000000000000000000000
%lf-(long double): -887936461558575470000000000000000000000.0000000000000000000000000000.00000000
%Lf-(long double): -887936461558575470000000000000000000000.0000000000000000000000000000.00000000
Note:
As noted in Linked Answer of @nekketsuuuuu's comment, apparently the bug (%lf
from MinGW 4.8 originating in TDM is long double
e>interpreted as a designation for ).
printf("%lf\n", (long double)v);
The expected value was displayed.(that is, it was interpreted as a designation for long double
)
(%Lf
which originally means long double
works as expected.)
There was information like specifying the __USE_MINGW_ANSI_STDIO
macro elsewhere.
At least it doesn't seem to work now.
(From Sayuri's answer, if -std=c99
, it is already specified.)
MinGW-w64 doesn't seem to have this problem, so switch the compiler or
Using -std=gnu99
instead of -std=c99
seems to be a simple solution.
(However, %zu
becomes unavailable)
-std=c99
(even though -std=c11
) It seems that switching reference headers when specifying standards is the reason why the behavior is different.
I wish I could switch between them without causing any problems from users, but I think it's difficult because there are many things involved.
%lf
is displayed incorrectly", but "l" in printf()
is simply ignored in ISO-C99, so it is useless.Also, Microsoft says that %lf
will be %Lf
(double long). Use %f
" comments: https://sourceforge.net/p/mingw/bugs/2253/_USE_MINGW_ANSI_STDIO
Why don't you use Stack Overflow Answers%lf
is displayed incorrectly", but "l" in printf()
is simply ignored in ISO-C99, so it is useless.Also, Microsoft says that %lf
will be %Lf
(double long). Use %f
" comments: https://sourceforge.net/p/mingw/bugs/2253/_USE_MINGW_ANSI_STDIO
Why don't you use Stack Overflow Answers
© 2024 OneMinuteCode. All rights reserved.