lmi-commits
[Top][All Lists]
Advanced

[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__"



reply via email to

[Prev in Thread] Current Thread [Next in Thread]