sscanf won't scan hexadecimal formatted float with "%a" under mingw - c

I am using gcc 4.6.1 with mingw in windows.
I recently learned about the %a format specifier for representing floats exactly in floating-point hex notation (in C99, apparently).
For some reason I can print a float this way just fine, but I am having trouble scanning it.
If I replace both instances of %a, with %f my code handles printing and scanning the the floating point number just fine.
The code seems to work just fine when I ran it on a traditional Unix system (gcc 4.2.1).
I have tried every variation I could think of with floats and doubles. I tried adding the flag -std=c99, which seemed to change whether it treated the numeric literal as a double or float, but it did not fix my problem.
I want to know 2 things, if anyone else can duplicate this problem, and what I might do to fix it.
#include <stdio.h>
int main(){
char buffer[1000];
sprintf(buffer, "%la", 21.3930);
double x = 0;
printf("\n");
printf(buffer);
if(!sscanf(buffer, "%la", &x))
printf("\nscan failed.");
printf("\n%la", x);
return 0;
}
The output is:
0x1.5649bap+4
scan failed.
0x0.000000p+0
Help would be greatly appreciated.

MinGW uses msvcrt.dll for the major part of its C runtime support. msvcrt.dll generally doesn't support C99 functionality.
There are some versions of MinGW that provide their own support for C99 I/O specifiers, but I haven't collected good information on exactly what versions support what C99 features.
See the following for some additional details:
https://stackoverflow.com/a/7136886/12711
https://stackoverflow.com/a/10680635/12711
Update: It looks like the TDM-64 distribution of MinGW (version 4.6.1) supports the C99 specifiers in the scanf() family of functions with prefixed names such as:
__mingw_sscanf()
__mingw_scanf()
It might be that support is improved in versions newer than 4.6.1 (it looks like that at least as of version 4.6.1 these functions are not in the 32-bit version of the TDM distribution - keep in mind that TDM-64 is capable of building 32-bit binaries as well as 64-bit binaries).
You can use a simple set of macros to 'redirect' the normal names to these functions.
Example output:
C:\temp>gcc --version
gcc (tdm64-1) 4.6.1
C:\temp>gcc -m32 -std=gnu99 -o test test.c
C:\temp>test
0xa.b24dd2f1a9fcp+1
0xa.b24dd2f1a9fcp+1

Related

msvcrt alternative for MinGW? (e.g. to get conforming snprintf)

So here's a fun one...
we have a few C libraries which should be platform independent, even though they were developed on linux, because they only rely on the c standard library as defined in ISO/IEC 9899:1999. When we compiled those libraries with MinGW everything seemed to work fine at first, but today we found out that the snprintf() implementation of msvcrt is braindea... sorry, i meant "incompatible" with the definition in the C99 standard.
I would have expected a warning from MinGW, telling me that -std=c99 actually isn't fully supported. Because otherwise, how am i supposed to know?
Is there any alternative c standard library for windows, and most importantly: can MinGW somehow be told to link against it instead of msvcrt?
If not, we would at the very least need a list or something where we can check for other potential portability problems concerning msvcrt and c99.
PS:
I know about Cygwin and MSYS2, but i'd rather have native windows binaries (in part because we also use our libraries in Matlab).
Edit:
Sorry, i should have explained more clearly what exactly my problem with msvcrt's snprintf() is. According to the standard, snprintf() is required to output a '\0' as the last character, if the output doesn't fit. However, msvcrt just doesn't do that, so the resulting string is no longer properly terminated. I have no idea why anyone would choose to implement snprintf() that way, because to me just omitting the '\0' doesn't make any sense at all.
We have also tried the suggested __USE_MINGW_ANSI_STDIO, but i guess that just fixes the missing format specifiers? At least it didn't seem to make a difference for our specific problem.
The standard enforces snprintf to behave like this:
Otherwise, output characters beyond the n-1st are
discarded rather than being written to the array, and a null character is written at the end of the characters actually written into the array.
snprintf in msvcrt is indeed not the standard one, but a Microsoft version as explained here:
Is snprintf() ALWAYS null terminating?
The following code gives non-compliant results:
#include <stdio.h>
int main (void)
{
char dst[3];
snprintf(dst, 3, "%c%c%c", 'A', 'B', 'C');
for(size_t i=0; i<3; i++)
{
printf("%.2X ", dst[i]);
}
}
I get output 41 42 43 which is not standard compliant. To get standard C, you have to add this before the stdio.h include:
#define __USE_MINGW_ANSI_STDIO 1
And now you get 41 42 00 which is compliant.
The root of all these program security problems is Microsoft, who have been using non-compliant C libs in their products for the past 20 years.
If you need C99 stdio from MinGW-w64, you can define __USE_MINGW_ANSI_STDIO so that you bypass the msvcrt implementation. It's best to define this through a compiler argument
-D__USE_MINGW_ANSI_STDIO
Alternatively, you could try to use a MinGW-w64 build that is set up to link with the new ucrt, but I don't know of any pre-existing easy-to-use stable builds that are set up that way.

What is wrong with printf("%llx")?

I have this piece of code that is challenging all my knowledge of C.
Here I have :
int main(void){
unsigned long long int massage ;
scanf("%llX", &massage); //input: 0x1234567890abcdef
printf("%llX", massage);
return 0;
}
On my "64bit - Corei5 - Fedora - GCC" it prints out exactly what I fed it. but on my buddy's system (32bit, MS XP, MinGW) it prints 90ABCDEF. I don't understand why. does anyone know?
BTW: sizeof(unsigned long long int) on his system is 8.
The issue is a discrepancy between what the compiler believes (as reflected in sizeof: sizeof(unsigned long long int) is evaluated at compile-time) and what the run-time library believes (as reflected in printf: the printf function is called at run-time, so that's when its format-specifiers take effect).
According to "C99" in the MinGW documentation:
GCC does not include a C runtime library. This is supplied by the platform. The MinGW port of GCC uses Microsoft's original (old) Visual C runtime, MSVCRT, which was targeted by Microsoft Visual Studio 6 (released in 1998).
[…]
Because MinGW relies on MSVCRT, it has many of the same limitations and quirks with compatibility as Visual Studio 6. You should assume that MinGW applications cannot rely on C99 behaviour, only on C89. For example, the newer format characters in printf like %a and %ll are not supported, although there exists a workaround for %ll.
(The workaround that it mentions is to use I64 instead of ll: so, %I64X. Annoyingly, at least on my system, GCC will issue a warning when it sees that in a literal format-string, because it assumes it'll have a better run-time library.)
The Windows C library uses "%I64d", not "%lld", to print arguments of
type "long long".
Ref: http://gcc.gnu.org/ml/gcc-patches/2004-11/msg01966.html

printf and %llx in GCC under Windows 64x

I am trying to get rid of a bogus warning in my program. Under windows 64 (under linux there is no such warning) this statement:
printf("%llu",UINT64_MAX);
generates the following warning:
warning: unknown conversion type character 'l' in format [-Wformat]|
The output appears to be correct and the warning should not be there. The most relevant gcc related post I can find is this bug report back from 2008 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=37768
If I understand correctly according to that, this warning stems from the fact that under the hood gcc calls MSVC printf which is not C99 compliant and can't understand the unsigned long long format in printf. From the same page the suggested solution is to use something called gnu_printf. I tried to google that but I did not find a header to include.
So the question is how should this be handled in order to be portable? I just want to get rid of the warning in a correct and non-sloppy way.
As I'm guessing you probably already know, from http://comments.gmane.org/gmane.comp.gnu.mingw.w64.general/4670 (note: dead link; see the Internet Archive's copy),
the issue is that formatter-width specifier %ll isn't supported for
all msvcrt-DLL versions, therefore gcc warns about its use. The
variant for specifying 64-bit integer-scalar-width in formatter for
msvcrt in a backward-compatible way is by using %I64.
With gcc 6.2.1 you can use -fno-builtin option.
That option makes gcc think that printf isn't built-in (which is the case, after all), and the warning goes away, printf is treated just like any function you'd have written.
Careful though, as no format check is performed anymore in that case, and there are other side-effects as there are other built-ins covered by that option.
A better alternative is not to use Microsoft version of printf at all by setting the macro __USE_MINGW_ANSI_STDIO with:
gcc -D__USE_MINGW_ANSI_STDIO=1 ...
(ref: C program shows %zu after conversion to Windows). It allows to use %zu specifier, that Microsoft printf doesn't support and you keep formatting warnings.

MinGW GCC: "Unknown conversion type character 'h'" (snprintf)

Okay, I've run into a strange issue compiling a C file with MinGW (GCC 4.6.2) on Windows 7. The file in question contains the following C code:
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("%2hhX\n", 250);
char c[80];
snprintf(c, sizeof(c), "%2hhX", 250);
printf("%s\n", c);
return 0;
}
The compilation turns out like this:
$ gcc.exe -std=c99 -pedantic -Wall test.c
test.c: In function 'main':
test.c:6:2: warning: unknown conversion type character 'h' in format [-Wformat]
test.c:6:2: warning: too many arguments for format [-Wformat-extra-args]
Now, what's strange to me is that it complains about the snprintf call on line 6, but not the printf call on line 4. Am I missing something or is the warning just incorrect? Also, is there perhaps a better equivalent for the format string "%2hhX"? (I'm trying to print char variables as hexadecimal values.)
Historically, MinGW has been in a bit of an odd situation, especially as far as C99 support goes. MinGW relies mostly on the msvcrt.dll runtime that's distributed with Windows, and that runtime doesn't support C99.
So with older versions of MinGW, you can run into problems in C99 mode when using C99-specific format specifiers. Also historically, GCC didn't make any special accommodations for msvcrt.dll's lack of support for C99 specifiers. So you'd get into situations where -Wformat wouldn't warn about a format that wouldn't work.
Things are improving on both sides - GCC has specific support for -Wformat when used with the MS runtime, such as:
-Wpedantic-ms-format so that GCC won't complain about "I32" and "I64" (even though it's documented, I still get a complaint about it being unrecognized even in 4.7.0 - maybe it's brand new)
the ms_printf option to __attribute__((__format__))
On the other side, MinGW has provided its own snprintf() for a while, since MSVC's variant, _snprintf(), behaves quite differently. However, MinGW relied for a long while on the printf() in msvcrt.dll, so C99 format specifiers for printf() didn't work. At some point MinGW started providing it's own version of printf() and friends so that you could get proper C99 (and GNU?) support. However, it seems that to be on the conservative side, these didn't replace the msvcrt.dll versions initially. They have names like __mingw_printf().
It looks like at some point between 4.6.1 and 4.7.0, the MinGW headers started using the MinGW supplied versions as replacements for the msvcrt.dll function (at least if you've specifed C99).
However, it seems that with the newer versions, GCC and MinGW are still a little out of sync. Where as before GCC would not warn about specifiers that wouldn't actually work on MinGW, not it complains about spcifiers that will.
You may want to try the following snipet of code to see how well your version of MinGW support "hhX":
printf("%hhX\n", 0x11223344);
__mingw_printf("%hhX\n", 0x11223344);
I'm not sure what to suggest to fix the problem you're running into - I think that you may be able to patch the MinGW stdio.h header so that it has a __attribute__((__format__ (gnu_printf, ...))) attribute on the printf functions (they're not there in the newer stdio.h, so GCC will use it's default idea of what the format support is).
In addition to the other answer, here is some more info on printf format checks in GCC:
When you say __attribute__((__format__ (FORMAT, ...))), the value of FORMAT can be (as far as printf is concerned) one of the following: printf, gnu_printf, ms_printf.
ms_printf makes GCC assume that function takes a format string intended for Microsoft Visual Studio CRT printf family functions. It means that GCC will complain about z, hh and ll, but will pass I64 without warning.
gnu_printf makes GCC assume GNU libc printf implementation underneath (or maybe just a POSIX/C99-compliant printf implementation, i'm not sure). Therefore GCC will complain about I64 and other Microsoft extensions, but will accept z, hh and ll.
printf is an alias for ms_printf when compiling for Windows, and an alias for gnu_printf otherwise.
Note that this check is completely orthogonal to the actual printf implementation being used. This is easy to see if you write your own printf-like function and put __attribute__((__format__ (FORMAT, ...))) on it - GCC will complain about different things depending on FORMAT, but you can do whatever you want inside the function.
Available printf implementations that i know of:
MinGW ANSI STDIO (compile with -D__USE_MINGW_ANSI_STDIO=1) in MinGW.org and MinGW-w64 toolchains. Complies with ms_printf (fully?) and gnu_printf format (partially - does not support positional arguments).
MSVCRT (compile without -D__USE_MINGW_ANSI_STDIO=1). Complies with ms_printf (duh...), compliance with gnu_printf is very low and depends on runtime version (old versions did not support ll, new ones do; z and hh are not supported in any version so far; GCC is blissfully unaware of these developments though, and assumes the worst case, msvcrt from VC 6.0 era, it seems).
gnulib. Complies with ms_printf and gnu_printf completely (or near-completely).
The stdio.h header in MinGW.org does not use attribute format.
The stdio.h header in MinGW-w64 uses attribute format gnu_printf for MinGW ANSI STDIO implementation, but does not use anything for MSVCRT implementation. FIXED: In newer versions of MinGW-w64 headers stdio.h will use attribute format ms_printf for MSVCRT implementation.
gnulib is fully aware of the difference between printf and gnu_printf, and will pick one or the other depending on some complicated macros (presumably, accompanying it with a proper implementation that supports what the format says it does).
Pieces of software that are known (at the moment) to have problems with GCC format checks:
glib - uses printf format, but implementation is from gnulib; there's an outstanding bug for changing it to gnu_printf
CPython - the code is full of z formats, but official binaries are built against MSVCRT; it also uses printf format in its extension headers, even though extensions often use z as well

mingw printf size specification character h

Visual Studio has a size and distance specification chart that says I can do something like this, using the h prefix to specify single byte character string regardless of printf or wprintf:
int main()
{
char test[]= "abc";
_tprintf(_T("%hs\n"),test);
}
But if I compile the same thing with mingw gcc and -Wall I get the following warning:
warning: format '%hs' expects type 'short int *', but argument 2 has type 'char *'
Is what I'm doing an acceptable way to specify a single byte character string in mingw?
Thanks
Edit-This question has been answered below in fair detail. The short answer is yes, it's an acceptable way to specify a single byte character string in mingw and the warning can be ignored.
You'll also note that the Visual Studio docs say:
Note: The h and l prefixes are Microsoft extensions when used with data of type char.
I think that this would be more accurate to say, "when used with the 'c' or 's' format specifiers (upper or lower case)".
Realize that the GCC compiler really doesn't know about how Microsoft's runtime deals with printf() format strings and the warning GCC is giving is tailored to the runtimes that it more normally uses. When building with MinGW, a Microsoft C runtime that's provided with Windows, msvcrt.dll, is used (though MinGW does provide replacements or wrappers for some library functions). So there is sometimes confusing between what the compiler thinks is OK for a printf() format string and the arguments provided to printf() and what the runtime will actually do.
For example, until recently, using ll on an integer conversion to format a 64-bit int type wouldn't work correctly in MinGW. I'm not sure if the 'fix' for that occurred in the MinGW support functions or if msvcrt.dll was updated to support the ll modifier (I suspect that msvcrt.dll was updated - I'll have to check...).
Anyway, what this boils down to is that regardless of the warnings that GCC gives about the printf() format string, in all likelihood you'll need to use the MSVC docs for format strings, since its an MS runtime that MinGW will be using. If you don't like the warngin being generated you might want to consider adding the -Wno-format option to your MinGW build script.

Resources