]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - misc/efgcvt_r.c
elf: Never use the file ID of the main executable [BZ #24900]
[thirdparty/glibc.git] / misc / efgcvt_r.c
index 7f80242580306fe4cc55296e60b50b83696f829b..981249850cdd9f37d740df981a862544d43232ec 100644 (file)
@@ -1,53 +1,82 @@
-/* [efg]cvt -- compatibility functions for floating point formatting,
-   reentrant versions.
-Copyright (C) 1995, 1996 Free Software Foundation, Inc.
-This file is part of the GNU C Library.
-
-The GNU C Library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public License as
-published by the Free Software Foundation; either version 2 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
-Library General Public License for more details.
-
-You should have received a copy of the GNU Library General Public
-License along with the GNU C Library; see the file COPYING.LIB.  If
-not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+/* Compatibility functions for floating point formatting, reentrant versions.
+   Copyright (C) 1995-2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   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
+   <https://www.gnu.org/licenses/>.  */
 
 #include <errno.h>
+#include <float.h>
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
 #include <math.h>
 #include <stdlib.h>
+#include <sys/param.h>
+#include <math_ldbl_opt.h>
 
 #ifndef FLOAT_TYPE
-#define FLOAT_TYPE double
-#define FUNC_PREFIX
-#define FLOAT_FMT_FLAG
-#define FLOAT_NAME_EXT
+# define FLOAT_TYPE double
+# define FUNC_PREFIX
+# define FLOAT_FMT_FLAG
+# define FLOAT_NAME_EXT
+# define FLOAT_MIN_10_EXP DBL_MIN_10_EXP
+# if DBL_MANT_DIG == 53
+#  define NDIGIT_MAX 17
+# elif DBL_MANT_DIG == 24
+#  define NDIGIT_MAX 9
+# elif DBL_MANT_DIG == 56
+#  define NDIGIT_MAX 18
+# else
+/* See IEEE 854 5.6, table 2 for this formula.  Unfortunately we need a
+   compile time constant here, so we cannot use it.  */
+#  error "NDIGIT_MAX must be precomputed"
+#  define NDIGIT_MAX (lrint (ceil (M_LN2 / M_LN10 * DBL_MANT_DIG + 1.0)))
+# endif
+# if DBL_MIN_10_EXP == -37
+#  define FLOAT_MIN_10_NORM    1.0e-37
+# elif DBL_MIN_10_EXP == -307
+#  define FLOAT_MIN_10_NORM    1.0e-307
+# elif DBL_MIN_10_EXP == -4931
+#  define FLOAT_MIN_10_NORM    1.0e-4931
+# else
+/* libc can't depend on libm.  */
+#  error "FLOAT_MIN_10_NORM must be precomputed"
+#  define FLOAT_MIN_10_NORM    exp10 (DBL_MIN_10_EXP)
+# endif
+#else
+# define LONG_DOUBLE_CVT
 #endif
 
 #define APPEND(a, b) APPEND2 (a, b)
 #define APPEND2(a, b) a##b
+#define __APPEND(a, b) __APPEND2 (a, b)
+#define __APPEND2(a, b) __##a##b
 
 #define FLOOR APPEND(floor, FLOAT_NAME_EXT)
 #define FABS APPEND(fabs, FLOAT_NAME_EXT)
 #define LOG10 APPEND(log10, FLOAT_NAME_EXT)
+#define EXP APPEND(exp, FLOAT_NAME_EXT)
 
 
 int
-APPEND (FUNC_PREFIX, fcvt_r) (value, ndigit, decpt, sign, buf, len)
-     FLOAT_TYPE value;
-     int ndigit, *decpt, *sign;
-     char *buf;
-     size_t len;
+__APPEND (FUNC_PREFIX, fcvt_r) (FLOAT_TYPE value, int ndigit, int *decpt,
+                               int *sign, char *buf, size_t len)
 {
-  int n, i;
+  ssize_t n;
+  ssize_t i;
+  int left;
 
   if (buf == NULL)
     {
@@ -55,54 +84,179 @@ APPEND (FUNC_PREFIX, fcvt_r) (value, ndigit, decpt, sign, buf, len)
       return -1;
     }
 
-  *sign = value < 0.0;
-  if (*sign)
-    value = - value;
+  left = 0;
+  if (isfinite (value))
+    {
+      *sign = signbit (value) != 0;
+      if (*sign)
+       value = -value;
+
+      if (ndigit < 0)
+       {
+         /* Rounding to the left of the decimal point.  */
+         while (ndigit < 0)
+           {
+             FLOAT_TYPE new_value = value * 0.1;
+
+             if (new_value < 1.0)
+               {
+                 ndigit = 0;
+                 break;
+               }
+
+             value = new_value;
+             ++left;
+             ++ndigit;
+           }
+       }
+    }
+  else
+    /* Value is Inf or NaN.  */
+    *sign = 0;
 
-  n = snprintf (buf, len, "%.*" FLOAT_FMT_FLAG "f", ndigit, value);
-  if (n < 0)
+  n = __snprintf (buf, len, "%.*" FLOAT_FMT_FLAG "f", MIN (ndigit, NDIGIT_MAX),
+                 value);
+  /* Check for a too small buffer.  */
+  if (n >= (ssize_t) len)
     return -1;
 
   i = 0;
   while (i < n && isdigit (buf[i]))
     ++i;
   *decpt = i;
-  do
-    ++i;
-  while (! isdigit (buf[i]));
-  memmove (&buf[i - *decpt], buf, n - (i - *decpt));
+
+  if (i == 0)
+    /* Value is Inf or NaN.  */
+    return 0;
+
+  if (i < n)
+    {
+      do
+       ++i;
+      while (i < n && !isdigit (buf[i]));
+
+      if (*decpt == 1 && buf[0] == '0' && value != 0.0)
+       {
+         /* We must not have leading zeroes.  Strip them all out and
+            adjust *DECPT if necessary.  */
+         --*decpt;
+         while (i < n && buf[i] == '0')
+           {
+             --*decpt;
+             ++i;
+           }
+       }
+
+      memmove (&buf[MAX (*decpt, 0)], &buf[i], n - i);
+      buf[n - (i - MAX (*decpt, 0))] = '\0';
+    }
+
+  if (left)
+    {
+      *decpt += left;
+      if ((ssize_t) --len > n)
+       {
+         while (left-- > 0 && n < (ssize_t) len)
+           buf[n++] = '0';
+         buf[n] = '\0';
+       }
+    }
 
   return 0;
 }
 
-#define weak_extern2(name) weak_extern (name)
-weak_extern2 (FLOOR) weak_extern2 (LOG10) weak_extern2 (FABS)
-
 int
-APPEND (FUNC_PREFIX, ecvt_r) (value, ndigit, decpt, sign, buf, len)
-     FLOAT_TYPE value;
-     int ndigit, *decpt, *sign;
-     char *buf;
-     size_t len;
+__APPEND (FUNC_PREFIX, ecvt_r) (FLOAT_TYPE value, int ndigit, int *decpt,
+                               int *sign, char *buf, size_t len)
 {
-  FLOAT_TYPE (*log10_function) (FLOAT_TYPE) = &LOG10;
+  int exponent = 0;
 
-  if (log10_function)
-    {
-      /* Use the reasonable code if -lm is included.  */
-      ndigit -= (int) FLOOR (LOG10 (FABS (value)));
-      if (ndigit < 0)
-       ndigit = 0;
-    }
-  else
+  if (isfinite (value) && value != 0.0)
     {
       /* Slow code that doesn't require -lm functions.  */
       FLOAT_TYPE d;
-      for (d = value < 0.0 ? - value : value;
-          ndigit > 0 && d >= 10.0;
-          d *= 0.1)
-       --ndigit;
+      FLOAT_TYPE f = 1.0;
+      if (value < 0.0)
+       d = -value;
+      else
+       d = value;
+      /* For denormalized numbers the d < 1.0 case below won't work,
+        as f can overflow to +Inf.  */
+      if (d < FLOAT_MIN_10_NORM)
+       {
+         value /= FLOAT_MIN_10_NORM;
+         if (value < 0.0)
+           d = -value;
+         else
+           d = value;
+         exponent += FLOAT_MIN_10_EXP;
+       }
+      if (d < 1.0)
+       {
+         do
+           {
+             f *= 10.0;
+             --exponent;
+           }
+         while (d * f < 1.0);
+
+         value *= f;
+       }
+      else if (d >= 10.0)
+       {
+         do
+           {
+             f *= 10;
+             ++exponent;
+           }
+         while (d >= f * 10.0);
+
+         value /= f;
+       }
     }
+  else if (value == 0.0)
+    /* SUSv2 leaves it unspecified whether *DECPT is 0 or 1 for 0.0.
+       This could be changed to -1 if we want to return 0.  */
+    exponent = 0;
 
-  return APPEND (FUNC_PREFIX, fcvt_r) (value, ndigit, decpt, sign, buf, len);
+  if (ndigit <= 0 && len > 0)
+    {
+      buf[0] = '\0';
+      *decpt = 1;
+      *sign = isfinite (value) ? signbit (value) != 0 : 0;
+    }
+  else
+    if (__APPEND (FUNC_PREFIX, fcvt_r) (value, MIN (ndigit, NDIGIT_MAX) - 1,
+                                       decpt, sign, buf, len))
+      return -1;
+
+  *decpt += exponent;
+  return 0;
 }
+
+#if LONG_DOUBLE_COMPAT (libc, GLIBC_2_0)
+# ifdef LONG_DOUBLE_CVT
+#  define cvt_symbol(symbol) \
+  cvt_symbol_1 (libc, __APPEND (FUNC_PREFIX, symbol), \
+             APPEND (FUNC_PREFIX, symbol), GLIBC_2_4)
+#  define cvt_symbol_1(lib, local, symbol, version) \
+    libc_hidden_def (local) \
+    versioned_symbol (lib, local, symbol, version)
+# else
+#  define cvt_symbol(symbol) \
+  cvt_symbol_1 (libc, __APPEND (FUNC_PREFIX, symbol), \
+             APPEND (q, symbol), GLIBC_2_0); \
+  weak_alias (__APPEND (FUNC_PREFIX, symbol), APPEND (FUNC_PREFIX, symbol))
+#  define cvt_symbol_1(lib, local, symbol, version) \
+  libc_hidden_def (local) \
+  compat_symbol (lib, local, symbol, version)
+# endif
+#else
+# define cvt_symbol(symbol) \
+  cvt_symbol_1 (__APPEND (FUNC_PREFIX, symbol), APPEND (FUNC_PREFIX, symbol))
+#  define cvt_symbol_1(local, symbol) \
+  libc_hidden_def (local) \
+  weak_alias (local, symbol)
+#endif
+cvt_symbol(fcvt_r);
+cvt_symbol(ecvt_r);