libcxx + MingW: correct printout of long doubles

Yaron Keren yaron.keren at gmail.com
Fri Nov 15 14:03:32 PST 2013


(this is slightly modfied version of patch posted on 4 Oct 2013)

Long ago Microsoft had decided (
http://msdn.microsoft.com/en-us/library/9cx8xs15.aspx) to make thelong
double type in Visual C++ same as double, 64 bit. This was an incredibly
stupid decision, at least for those of us who actually care about numeric
accuracy.

MingW, gcc-based port for Windows, had kept 80-bit long doubles and so does
clang. This is a good decision which enable using these compilers in
numeric code where 80 bit long doubles are required.

However, MingW uses the Microsoft runtime DLL msvcrt.dll. Here lies a
problem: while gcc creates 80 bits long doubles, the MS runtime accepts 64
bit long doubles only. The problem is usually not obvious since the long
double is casted to a double, as in:

 exp(10.0L)

where the 10.0L will be silently cast to 10.0 and the routine will seem to
work.

However, in the *printf and *scanf family of functions arguments are not
downcasted from long double to double (float does cast to double). For
instance, naively compiling

 printf("%Lf\n",2345.0L);

with gcc or clang with the MingW runtime will print garbage results.

The MingW developers did develop a solution to the problem, namely
alternative __mingw_printf and __mingw_scanf family of functions that
accept long double 80 bit arguments properly.  In order to use the correct
routines, one has to

#define __USE_MINGW_ANSI_STDIO 1

which is not the default in libc but it is defined in gcc libstdc++ library
header "os_defines.h" as default.

However, for snprintf_l there is no MingW alternative, it is simply
#defined to _snprintf_l, a function from msvcrt.dll which does not support
80 bit long doubles.

This patch implements snprintf_l  function in a way similar to the other
functions in src/support/win32/locale_win32.cpp and locale_win32.h, calling
upon vsnprintf for which there is a MingW correct alternative.

*Note! *__USE_MINGW_ANSI_STDIO is not modified in this patch. In order to
use the __mingw version it must be defined before including the MingW
headers.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20131116/033e9654/attachment.html>
-------------- next part --------------
Index: include/support/win32/locale_win32.h
===================================================================
--- include/support/win32/locale_win32.h	(revision 194657)
+++ include/support/win32/locale_win32.h	(working copy)
@@ -103,9 +103,9 @@
 #define sscanf_l( __s, __l, __f, ...) _sscanf_l( __s, __f, __l, __VA_ARGS__ )
 #define vsscanf_l( __s, __l, __f, ...) _sscanf_l( __s, __f, __l, __VA_ARGS__ )
 #define sprintf_l( __s, __l, __f, ... ) _sprintf_l( __s, __f, __l, __VA_ARGS__ )
-#define snprintf_l( __s, __n, __l, __f, ... ) _snprintf_l( __s, __n, __f, __l, __VA_ARGS__ )
 #define vsprintf_l( __s, __l, __f, ... ) _vsprintf_l( __s, __f, __l, __VA_ARGS__ )
 #define vsnprintf_l( __s, __n, __l, __f, ... ) _vsnprintf_l( __s, __n, __f, __l, __VA_ARGS__ )
+int snprintf_l(char *ret, size_t n, locale_t loc, const char *format, ...);
 int asprintf_l( char **ret, locale_t loc, const char *format, ... );
 int vasprintf_l( char **ret, locale_t loc, const char *format, va_list ap );
 
Index: src/support/win32/locale_win32.cpp
===================================================================
--- src/support/win32/locale_win32.cpp	(revision 194657)
+++ src/support/win32/locale_win32.cpp	(working copy)
@@ -80,6 +80,16 @@
     return wctob( c );
 }
 
+int snprintf_l(char *ret, size_t n, locale_t loc, const char *format, ...)
+{
+    __locale_raii __current( uselocale(loc), uselocale );
+    va_list ap;
+    va_start( ap, format );
+    int result = vsnprintf( ret, n, format, ap );
+    va_end(ap);
+    return result;
+}
+
 int asprintf_l( char **ret, locale_t loc, const char *format, ... )
 {
     va_list ap;


More information about the cfe-commits mailing list