[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master b6e4a8f 1/2: Fix a MinGW-w64 roundl() error
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master b6e4a8f 1/2: Fix a MinGW-w64 roundl() error |
Date: |
Thu, 22 Dec 2016 13:43:40 +0000 (UTC) |
branch: master
commit b6e4a8f898df454feb5966bcb0797423f99eea1f
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>
Fix a MinGW-w64 roundl() error
* round_glibc.cpp: new source file with round*() replacement
* Makefile.am: build with new source file
* objects.make: likewise
* config.hpp: detect MinGW-w64 specifically
* test_coding_rules.cpp: allow certain gcc and glibc macros
---
Makefile.am | 2 +
config.hpp | 13 +-
objects.make | 2 +
round_glibc.cpp | 394 +++++++++++++++++++++++++++++++++++++++++++++++++
test_coding_rules.cpp | 4 +
5 files changed, 411 insertions(+), 4 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index c222c97..f275520 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -348,6 +348,7 @@ liblmi_common_sources = \
path_utility.cpp \
premium_tax.cpp \
progress_meter.cpp \
+ round_glibc.cpp \
sigfpe.cpp \
single_cell_document.cpp \
surrchg_rates.cpp \
@@ -973,6 +974,7 @@ test_regex_LDADD = \
test_round_SOURCES = \
$(common_test_objects) \
+ round_glibc.cpp \
round_test.cpp
test_round_CXXFLAGS = $(AM_CXXFLAGS)
diff --git a/config.hpp b/config.hpp
index 8c27456..9094463 100644
--- a/config.hpp
+++ b/config.hpp
@@ -118,11 +118,16 @@ namespace fs = boost::filesystem;
# include <_mingw.h>
#endif // __MINGW32__
-// This is RTL version, not gcc version. As to MinGW-w64, see:
+// lmi used the mingw.org toolchain through gcc-3.4.5, and switched
+// to MinGW-w64's gcc-4.9.2 circa 2016-01-01. To distinguish one from
+// the other, see:
// http://comments.gmane.org/gmane.comp.gnu.mingw.w64.general/641
-// No attempt is made to distinguish the MinGW-w64 versus mingw.org
-// toolchains: lmi used the mingw.org toolchain through gcc-3.4.5,
-// and switched to MinGW-w64's gcc-4.9.2 circa 2016-01-01.
+// https://sourceforge.net/p/mingw-w64/mailman/message/28485906/
+#if defined __MINGW64_VERSION_MAJOR
+# define LMI_MINGW_W64
+#endif // defined __MINGW64_VERSION_MAJOR
+
+// This is RTL version, not gcc version.
#if defined __MINGW32_VERSION || defined __MINGW64_VERSION_MAJOR
# define LMI_MINGW_VERSION \
(__MINGW32_MAJOR_VERSION * 100 + __MINGW32_MINOR_VERSION)
diff --git a/objects.make b/objects.make
index 69327c8..d8e9d7f 100644
--- a/objects.make
+++ b/objects.make
@@ -237,6 +237,7 @@ common_common_objects := \
path_utility.o \
premium_tax.o \
progress_meter.o \
+ round_glibc.o \
sigfpe.o \
single_cell_document.o \
surrchg_rates.o \
@@ -866,6 +867,7 @@ regex_test$(EXEEXT): \
round_test$(EXEEXT): \
$(common_test_objects) \
+ round_glibc.o \
round_test.o \
round_to_test$(EXEEXT): \
diff --git a/round_glibc.cpp b/round_glibc.cpp
new file mode 100644
index 0000000..7f9810a
--- /dev/null
+++ b/round_glibc.cpp
@@ -0,0 +1,394 @@
+// Standard C round*() from glibc to fix MinGW-w64 defects.
+//
+// Copyright (C) 2016 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+// Analects of glibc's rounding implementation, gathered from relevant
+// portions of the glibc sources on 2016-12-22. URLs for the originals
+// are given in comments above each copied block. GWC is responsible
+// for any defect of colligation, which should not reflect on the
+// reputations of the original authors. Headers copied in relevant
+// part rather than included, for compactness. Reformatted for
+// concinnity with lmi. Function names de-uglified for direct use;
+// aliasing removed.
+
+// Motivation: roundl(0.499999999999999999973L) should return zero,
+// but the MinGW-w64 implementation as of 2016-12 returns one instead
+// (unless the rounding mode, which should be irrelevant, is toward
+// infinity). See:
+// http://lists.nongnu.org/archive/html/lmi/2016-12/msg00042.html
+// https://sourceforge.net/p/mingw-w64/bugs/573/
+
+#include "lmi.hpp" // [to detect MinGW-w64]
+
+#if defined __MINGW64_VERSION_MAJOR
+
+#include <math.h>
+#include <stdint.h>
+
+// This prestandard typedef appears to have originated in BSD:
+//
https://lists.freedesktop.org/archives/release-wranglers/2004-August/000925.html
+typedef uint32_t u_int32_t;
+
+// This file is intended only for use with MinGW-w64, which is always
+// little endian. It seemed best to preserve alternative definitions
+// in case they're ever wanted for some unanticipated purpose, and
+// to provide these alii mapping the macros used in the unmodified
+// sources to gcc's standard predefinitions:
+#define __FLOAT_WORD_ORDER __FLOAT_WORD_ORDER__
+#define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
+#define BIG_ENDIAN __ORDER_BIG_ENDIAN__
+
+//
https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/ieee754/dbl-64/s_round.c
+/* Round double to integer away from zero. */
+//
https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/ieee754/flt-32/s_roundf.c
+/* Round float to integer away from zero. */
+//
https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=sysdeps/ieee754/ldbl-96/s_roundl.c
+/* Round long double to integer away from zero.
+ Copyright (C) 1997-2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <address@hidden>, 1997.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+//
https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=sysdeps/generic/math_private.h
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+/* A union which permits us to convert between a double and two 32 bit
+ ints. */
+
+#if __FLOAT_WORD_ORDER == BIG_ENDIAN
+typedef union
+{
+ double value;
+ struct
+ {
+ u_int32_t msw;
+ u_int32_t lsw;
+ } parts;
+ uint64_t word;
+} ieee_double_shape_type;
+#endif
+
+#if __FLOAT_WORD_ORDER == LITTLE_ENDIAN
+typedef union
+{
+ double value;
+ struct
+ {
+ u_int32_t lsw;
+ u_int32_t msw;
+ } parts;
+ uint64_t word;
+} ieee_double_shape_type;
+#endif
+
+/* Get two 32 bit ints from a double. */
+
+#define EXTRACT_WORDS(ix0,ix1,d) \
+do { \
+ ieee_double_shape_type ew_u; \
+ ew_u.value = (d); \
+ (ix0) = ew_u.parts.msw; \
+ (ix1) = ew_u.parts.lsw; \
+} while (0)
+
+/* Set a double from two 32 bit ints. */
+#ifndef INSERT_WORDS
+# define INSERT_WORDS(d,ix0,ix1) \
+do { \
+ ieee_double_shape_type iw_u; \
+ iw_u.parts.msw = (ix0); \
+ iw_u.parts.lsw = (ix1); \
+ (d) = iw_u.value; \
+} while (0)
+#endif
+
+/* A union which permits us to convert between a float and a 32 bit
+ int. */
+
+typedef union
+{
+ float value;
+ u_int32_t word;
+} ieee_float_shape_type;
+
+/* Get a 32 bit int from a float. */
+#ifndef GET_FLOAT_WORD
+# define GET_FLOAT_WORD(i,d) \
+do { \
+ ieee_float_shape_type gf_u; \
+ gf_u.value = (d); \
+ (i) = gf_u.word; \
+} while (0)
+#endif
+
+/* Set a float from a 32 bit int. */
+#ifndef SET_FLOAT_WORD
+# define SET_FLOAT_WORD(d,i) \
+do { \
+ ieee_float_shape_type sf_u; \
+ sf_u.word = (i); \
+ (d) = sf_u.value; \
+} while (0)
+#endif
+
+//
https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=sysdeps/ieee754/ldbl-96/math_ldbl.h
+
+/* A union which permits us to convert between a long double and
+ three 32 bit ints. */
+
+#if __FLOAT_WORD_ORDER == BIG_ENDIAN
+typedef union
+{
+ long double value;
+ struct
+ {
+ int sign_exponent:16;
+ unsigned int empty:16;
+ u_int32_t msw;
+ u_int32_t lsw;
+ } parts;
+} ieee_long_double_shape_type;
+#endif
+
+#if __FLOAT_WORD_ORDER == LITTLE_ENDIAN
+typedef union
+{
+ long double value;
+ struct
+ {
+ u_int32_t lsw;
+ u_int32_t msw;
+ int sign_exponent:16;
+ unsigned int empty:16;
+ } parts;
+} ieee_long_double_shape_type;
+#endif
+
+/* Get three 32 bit ints from a double. */
+
+#define GET_LDOUBLE_WORDS(exp,ix0,ix1,d) \
+do { \
+ ieee_long_double_shape_type ew_u; \
+ ew_u.value = (d); \
+ (exp) = ew_u.parts.sign_exponent; \
+ (ix0) = ew_u.parts.msw; \
+ (ix1) = ew_u.parts.lsw; \
+} while (0)
+
+/* Set a double from two 32 bit ints. */
+
+#define SET_LDOUBLE_WORDS(d,exp,ix0,ix1) \
+do { \
+ ieee_long_double_shape_type iw_u; \
+ iw_u.parts.sign_exponent = (exp); \
+ iw_u.parts.msw = (ix0); \
+ iw_u.parts.lsw = (ix1); \
+ (d) = iw_u.value; \
+} while (0)
+
+//
https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/ieee754/dbl-64/s_round.c
+
+double round(double x)
+{
+ int32_t i0, j0;
+ u_int32_t i1;
+
+ EXTRACT_WORDS (i0, i1, x);
+ j0 = ((i0 >> 20) & 0x7ff) - 0x3ff;
+ if (j0 < 20)
+ {
+ if (j0 < 0)
+ {
+ i0 &= 0x80000000;
+ if (j0 == -1)
+ i0 |= 0x3ff00000;
+ i1 = 0;
+ }
+ else
+ {
+ u_int32_t i = 0x000fffff >> j0;
+ if (((i0 & i) | i1) == 0)
+ /* X is integral. */
+ return x;
+
+ i0 += 0x00080000 >> j0;
+ i0 &= ~i;
+ i1 = 0;
+ }
+ }
+ else if (j0 > 51)
+ {
+ if (j0 == 0x400)
+ /* Inf or NaN. */
+ return x + x;
+ else
+ return x;
+ }
+ else
+ {
+ u_int32_t i = 0xffffffff >> (j0 - 20);
+ if ((i1 & i) == 0)
+ /* X is integral. */
+ return x;
+
+ u_int32_t j = i1 + (1 << (51 - j0));
+ if (j < i1)
+ i0 += 1;
+ i1 = j;
+ i1 &= ~i;
+ }
+
+ INSERT_WORDS (x, i0, i1);
+ return x;
+}
+
+//
https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/ieee754/flt-32/s_roundf.c
+
+float roundf(float x)
+{
+ int32_t i0, j0;
+
+ GET_FLOAT_WORD (i0, x);
+ j0 = ((i0 >> 23) & 0xff) - 0x7f;
+ if (j0 < 23)
+ {
+ if (j0 < 0)
+ {
+ i0 &= 0x80000000;
+ if (j0 == -1)
+ i0 |= 0x3f800000;
+ }
+ else
+ {
+ u_int32_t i = 0x007fffff >> j0;
+ if ((i0 & i) == 0)
+ /* X is integral. */
+ return x;
+
+ i0 += 0x00400000 >> j0;
+ i0 &= ~i;
+ }
+ }
+ else
+ {
+ if (j0 == 0x80)
+ /* Inf or NaN. */
+ return x + x;
+ else
+ return x;
+ }
+
+ SET_FLOAT_WORD (x, i0);
+ return x;
+}
+
+//
https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=sysdeps/ieee754/ldbl-96/s_roundl.c
+
+long double roundl(long double x)
+{
+ int32_t j0;
+ u_int32_t se, i1, i0;
+
+ GET_LDOUBLE_WORDS (se, i0, i1, x);
+ j0 = (se & 0x7fff) - 0x3fff;
+ if (j0 < 31)
+ {
+ if (j0 < 0)
+ {
+ se &= 0x8000;
+ i0 = i1 = 0;
+ if (j0 == -1)
+ {
+ se |= 0x3fff;
+ i0 = 0x80000000;
+ }
+ }
+ else
+ {
+ u_int32_t i = 0x7fffffff >> j0;
+ if (((i0 & i) | i1) == 0)
+ /* X is integral. */
+ return x;
+
+ u_int32_t j = i0 + (0x40000000 >> j0);
+ if (j < i0)
+ se += 1;
+ i0 = (j & ~i) | 0x80000000;
+ i1 = 0;
+ }
+ }
+ else if (j0 > 62)
+ {
+ if (j0 == 0x4000)
+ /* Inf or NaN. */
+ return x + x;
+ else
+ return x;
+ }
+ else
+ {
+ u_int32_t i = 0xffffffff >> (j0 - 31);
+ if ((i1 & i) == 0)
+ /* X is integral. */
+ return x;
+
+ u_int32_t j = i1 + (1 << (62 - j0));
+ if (j < i1)
+ {
+ u_int32_t k = i0 + 1;
+ if (k < i0)
+ {
+ se += 1;
+ k |= 0x80000000;
+ }
+ i0 = k;
+ }
+ i1 = j;
+ i1 &= ~i;
+ }
+
+ SET_LDOUBLE_WORDS (x, se, i0, i1);
+ return x;
+}
+
+#endif // defined __MINGW64_VERSION_MAJOR
+
diff --git a/test_coding_rules.cpp b/test_coding_rules.cpp
index c499a59..523daf5 100644
--- a/test_coding_rules.cpp
+++ b/test_coding_rules.cpp
@@ -781,11 +781,14 @@ bool check_reserved_name_exception(std::string const& s)
,"_vsnprintf"
,"_wcsdup"
// Compiler specific: gcc.
+ ,"__FLOAT_WORD_ORDER__"
,"__GLIBCPP__"
,"__GNUC_MINOR__"
,"__GNUC_PATCHLEVEL__"
,"__GNUC__"
,"__GNUG__"
+ ,"__ORDER_BIG_ENDIAN__"
+ ,"__ORDER_LITTLE_ENDIAN__"
,"__STRICT_ANSI__"
,"__asm__"
,"__attribute__"
@@ -806,6 +809,7 @@ bool check_reserved_name_exception(std::string const& s)
,"_LIBC"
,"__BIG_ENDIAN"
,"__BYTE_ORDER"
+ ,"__FLOAT_WORD_ORDER"
// Compiler specific: EDG; hence, como, and also libcomo.
,"__asm"
,"__COMO__"