From 72880ac182c8f3769f0be868f77166994282cb2a Mon Sep 17 00:00:00 2001 From: Andrew Gierth Date: Sat, 16 Feb 2019 01:50:16 +0000 Subject: [PATCH] Cygwin and Mingw floating-point fixes. Deal with silent-underflow errors in float4 for cygwin and mingw by using our strtof() wrapper; deal with misrounding errors by adding them to the resultmap. Some slight reorganization of declarations was done to avoid duplicating material between cygwin.h and win32_port.h. While here, remove from the resultmap all references to float8-small-is-zero; inspection of cygwin output suggests it's no longer required there, and the freebsd/netbsd/openbsd entries should no longer be necessary (these date back to c. 2000). This commit doesn't remove the file itself nor the documentation references for it; that will happen in a subsequent commit if all goes well. --- configure | 19 +++++++++++++++++++ configure.in | 13 +++++++++++++ src/include/port.h | 5 +++++ src/include/port/cygwin.h | 8 ++++++++ src/include/port/win32_port.h | 17 ++++++++++++----- src/port/strtof.c | 10 +++++++++- src/test/regress/resultmap | 7 ++----- 7 files changed, 68 insertions(+), 11 deletions(-) diff --git a/configure b/configure index 7291311ae38..849a7ebdf3d 100755 --- a/configure +++ b/configure @@ -15828,6 +15828,25 @@ fi +case $host_os in + # Cygwin and (apparently, based on test results) Mingw both + # have a broken strtof(), so substitute the same replacement + # code we use with VS2013. That's not a perfect fix, since + # (unlike with VS2013) it doesn't avoid double-rounding, but + # we have no better options. To get that, though, we have to + # force the file to be compiled despite HAVE_STRTOF. + mingw*|cygwin*) + case " $LIBOBJS " in + *" strtof.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS strtof.$ac_objext" + ;; +esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: On $host_os we will use our strtof wrapper." >&5 +$as_echo "$as_me: On $host_os we will use our strtof wrapper." >&6;} + ;; +esac + case $host_os in # Windows uses a specialised env handler diff --git a/configure.in b/configure.in index 8a55e8e8a59..71f8885fe22 100644 --- a/configure.in +++ b/configure.in @@ -1715,6 +1715,19 @@ AC_REPLACE_FUNCS(m4_normalize([ strtof ])) +case $host_os in + # Cygwin and (apparently, based on test results) Mingw both + # have a broken strtof(), so substitute the same replacement + # code we use with VS2013. That's not a perfect fix, since + # (unlike with VS2013) it doesn't avoid double-rounding, but + # we have no better options. To get that, though, we have to + # force the file to be compiled despite HAVE_STRTOF. + mingw*|cygwin*) + AC_LIBOBJ([strtof]) + AC_MSG_NOTICE([On $host_os we will use our strtof wrapper.]) + ;; +esac + case $host_os in # Windows uses a specialised env handler diff --git a/src/include/port.h b/src/include/port.h index 436bc83fe38..7e2004b178b 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -385,6 +385,11 @@ extern int isinf(double x); extern float strtof(const char *nptr, char **endptr); #endif +#ifdef HAVE_BUGGY_STRTOF +extern float pg_strtof(const char *nptr, char **endptr); +#define strtof(a,b) (pg_strtof((a),(b))) +#endif + #ifndef HAVE_MKDTEMP extern char *mkdtemp(char *path); #endif diff --git a/src/include/port/cygwin.h b/src/include/port/cygwin.h index 5c149470c68..f1fc1a93d76 100644 --- a/src/include/port/cygwin.h +++ b/src/include/port/cygwin.h @@ -16,3 +16,11 @@ #endif #define PGDLLEXPORT + +/* + * Cygwin has a strtof() which is literally just (float)strtod(), which means + * we get misrounding _and_ silent over/underflow. Using our wrapper doesn't + * fix the misrounding but does fix the error checks, which cuts down on the + * number of test variant files needed. + */ +#define HAVE_BUGGY_STRTOF 1 diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h index 9bab75d5da0..1438082723f 100644 --- a/src/include/port/win32_port.h +++ b/src/include/port/win32_port.h @@ -510,16 +510,23 @@ typedef unsigned short mode_t; #define isnan(x) _isnan(x) #endif -#if defined(_MSC_VER) && (_MSC_VER < 1900) +#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || \ + defined(__MINGW32__) || defined(__MINGW64__) /* * VS2013 has a strtof() that seems to give correct answers for valid input, * even on the rounding edge cases, but which doesn't handle out-of-range * input correctly. Work around that. + * + * Mingw claims to have a strtof, and my reading of its source code suggests + * that it ought to work (and not need this hack), but the regression test + * results disagree with me; whether this is a version issue or not is not + * clear. However, using our wrapper (and the misrounded-input variant file, + * already required for supporting ancient systems) can't make things any + * worse, except for a tiny performance loss when reading zeros. + * + * See also cygwin.h for another instance of this. */ -#define HAVE_BUGGY_WINDOWS_STRTOF 1 -extern float pg_strtof(const char *nptr, char **endptr); -#define strtof(a,b) (pg_strtof((a),(b))) - +#define HAVE_BUGGY_STRTOF 1 #endif /* Pulled from Makefile.port in MinGW */ diff --git a/src/port/strtof.c b/src/port/strtof.c index 7aca8e2e3df..9e37633bda5 100644 --- a/src/port/strtof.c +++ b/src/port/strtof.c @@ -52,7 +52,7 @@ strtof(const char *nptr, char **endptr) return fresult; } -#elif HAVE_BUGGY_WINDOWS_STRTOF +#elif HAVE_BUGGY_STRTOF /* * On Windows, there's a slightly different problem: VS2013 has a strtof() * that returns the correct results for valid input, but may fail to report an @@ -62,6 +62,14 @@ strtof(const char *nptr, char **endptr) * well, so prefer to round the strtod() result in such cases. (Normally we'd * just say "too bad" if strtof() doesn't support subnormals, but since we're * already in here fixing stuff, we might as well do the best fix we can.) + * + * Cygwin has a strtof() which is literally just (float)strtod(), which means + * we can't avoid the double-rounding problem; but using this wrapper does get + * us proper over/underflow checks. (Also, if they fix their strtof(), the + * wrapper doesn't break anything.) + * + * Test results on Mingw suggest that it has the same problem, though looking + * at the code I can't figure out why. */ float pg_strtof(const char *nptr, char **endptr) diff --git a/src/test/regress/resultmap b/src/test/regress/resultmap index 3bd1585a355..c766d03df23 100644 --- a/src/test/regress/resultmap +++ b/src/test/regress/resultmap @@ -1,6 +1,3 @@ -float8:out:i.86-.*-freebsd=float8-small-is-zero.out -float8:out:i.86-.*-openbsd=float8-small-is-zero.out -float8:out:i.86-.*-netbsd=float8-small-is-zero.out -float8:out:m68k-.*-netbsd=float8-small-is-zero.out -float8:out:i.86-pc-cygwin=float8-small-is-zero.out +float4:out:.*-.*-cygwin.*=float4-misrounded-input.out +float4:out:.*-.*-mingw.*=float4-misrounded-input.out float4:out:hppa.*-hp-hpux10.*=float4-misrounded-input.out -- 2.30.2