xref: /haiku/src/system/libroot/posix/glibc/misc/efgcvt_r.c (revision 69c4f5d707723661f43a90623eade65c228ccee9)
184b29b5fSFrançois Revol /* Compatibility functions for floating point formatting, reentrant versions.
284b29b5fSFrançois Revol    Copyright (C) 1995,96,97,98,99,2000,01,02 Free Software Foundation, Inc.
384b29b5fSFrançois Revol    This file is part of the GNU C Library.
484b29b5fSFrançois Revol 
584b29b5fSFrançois Revol    The GNU C Library is free software; you can redistribute it and/or
684b29b5fSFrançois Revol    modify it under the terms of the GNU Lesser General Public
784b29b5fSFrançois Revol    License as published by the Free Software Foundation; either
884b29b5fSFrançois Revol    version 2.1 of the License, or (at your option) any later version.
984b29b5fSFrançois Revol 
1084b29b5fSFrançois Revol    The GNU C Library is distributed in the hope that it will be useful,
1184b29b5fSFrançois Revol    but WITHOUT ANY WARRANTY; without even the implied warranty of
1284b29b5fSFrançois Revol    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1384b29b5fSFrançois Revol    Lesser General Public License for more details.
1484b29b5fSFrançois Revol 
1584b29b5fSFrançois Revol    You should have received a copy of the GNU Lesser General Public
1684b29b5fSFrançois Revol    License along with the GNU C Library; if not, write to the Free
1784b29b5fSFrançois Revol    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
1884b29b5fSFrançois Revol    02111-1307 USA.  */
1984b29b5fSFrançois Revol 
2084b29b5fSFrançois Revol #include <errno.h>
2184b29b5fSFrançois Revol #include <float.h>
22*69c4f5d7SAugustin Cavalier #include <stdio_private.h>
2384b29b5fSFrançois Revol #include <string.h>
2484b29b5fSFrançois Revol #include <ctype.h>
2584b29b5fSFrançois Revol #include <math.h>
2684b29b5fSFrançois Revol #include <stdlib.h>
2784b29b5fSFrançois Revol #include <sys/param.h>
2884b29b5fSFrançois Revol 
2984b29b5fSFrançois Revol #ifndef FLOAT_TYPE
3084b29b5fSFrançois Revol # define FLOAT_TYPE double
3184b29b5fSFrançois Revol # define FUNC_PREFIX
3284b29b5fSFrançois Revol # define FLOAT_FMT_FLAG
3384b29b5fSFrançois Revol # define FLOAT_NAME_EXT
3484b29b5fSFrançois Revol # if DBL_MANT_DIG == 53
3584b29b5fSFrançois Revol #  define NDIGIT_MAX 17
3684b29b5fSFrançois Revol # elif DBL_MANT_DIG == 24
3784b29b5fSFrançois Revol #  define NDIGIT_MAX 9
3884b29b5fSFrançois Revol # elif DBL_MANT_DIG == 56
3984b29b5fSFrançois Revol #  define NDIGIT_MAX 18
4084b29b5fSFrançois Revol # else
4184b29b5fSFrançois Revol /* See IEEE 854 5.6, table 2 for this formula.  Unfortunately we need a
4284b29b5fSFrançois Revol    compile time constant here, so we cannot use it.  */
4384b29b5fSFrançois Revol #  error "NDIGIT_MAX must be precomputed"
4484b29b5fSFrançois Revol #  define NDIGIT_MAX (lrint (ceil (M_LN2 / M_LN10 * DBL_MANT_DIG + 1.0)))
4584b29b5fSFrançois Revol # endif
4684b29b5fSFrançois Revol #endif
4784b29b5fSFrançois Revol 
4884b29b5fSFrançois Revol #define APPEND(a, b) APPEND2 (a, b)
4984b29b5fSFrançois Revol #define APPEND2(a, b) a##b
5084b29b5fSFrançois Revol 
5184b29b5fSFrançois Revol #define FLOOR APPEND(floor, FLOAT_NAME_EXT)
5284b29b5fSFrançois Revol #define FABS APPEND(fabs, FLOAT_NAME_EXT)
5384b29b5fSFrançois Revol #define LOG10 APPEND(log10, FLOAT_NAME_EXT)
5484b29b5fSFrançois Revol #define EXP APPEND(exp, FLOAT_NAME_EXT)
5584b29b5fSFrançois Revol 
5684b29b5fSFrançois Revol 
5784b29b5fSFrançois Revol int
5884b29b5fSFrançois Revol APPEND (FUNC_PREFIX, fcvt_r) (value, ndigit, decpt, sign, buf, len)
5984b29b5fSFrançois Revol      FLOAT_TYPE value;
6084b29b5fSFrançois Revol      int ndigit, *decpt, *sign;
6184b29b5fSFrançois Revol      char *buf;
6284b29b5fSFrançois Revol      size_t len;
6384b29b5fSFrançois Revol {
6484b29b5fSFrançois Revol   ssize_t n;
6584b29b5fSFrançois Revol   ssize_t i;
6684b29b5fSFrançois Revol   int left;
6784b29b5fSFrançois Revol 
6884b29b5fSFrançois Revol   if (buf == NULL)
6984b29b5fSFrançois Revol     {
7084b29b5fSFrançois Revol       __set_errno (EINVAL);
7184b29b5fSFrançois Revol       return -1;
7284b29b5fSFrançois Revol     }
7384b29b5fSFrançois Revol 
7484b29b5fSFrançois Revol   left = 0;
7584b29b5fSFrançois Revol   if (isfinite (value))
7684b29b5fSFrançois Revol     {
7784b29b5fSFrançois Revol       *sign = signbit (value) != 0;
7884b29b5fSFrançois Revol       if (*sign)
7984b29b5fSFrançois Revol 	value = -value;
8084b29b5fSFrançois Revol 
8184b29b5fSFrançois Revol       if (ndigit < 0)
8284b29b5fSFrançois Revol 	{
8384b29b5fSFrançois Revol 	  /* Rounding to the left of the decimal point.  */
8484b29b5fSFrançois Revol 	  while (ndigit < 0)
8584b29b5fSFrançois Revol 	    {
8684b29b5fSFrançois Revol 	      FLOAT_TYPE new_value = value * 0.1;
8784b29b5fSFrançois Revol 
8884b29b5fSFrançois Revol 	      if (new_value < 1.0)
8984b29b5fSFrançois Revol 		{
9084b29b5fSFrançois Revol 		  ndigit = 0;
9184b29b5fSFrançois Revol 		  break;
9284b29b5fSFrançois Revol 		}
9384b29b5fSFrançois Revol 
9484b29b5fSFrançois Revol 	      value = new_value;
9584b29b5fSFrançois Revol 	      ++left;
9684b29b5fSFrançois Revol 	      ++ndigit;
9784b29b5fSFrançois Revol 	    }
9884b29b5fSFrançois Revol 	}
9984b29b5fSFrançois Revol     }
10084b29b5fSFrançois Revol   else
10184b29b5fSFrançois Revol     /* Value is Inf or NaN.  */
10284b29b5fSFrançois Revol     *sign = 0;
10384b29b5fSFrançois Revol 
10484b29b5fSFrançois Revol   n = __snprintf (buf, len, "%.*" FLOAT_FMT_FLAG "f", MIN (ndigit, NDIGIT_MAX),
10584b29b5fSFrançois Revol 		  value);
10684b29b5fSFrançois Revol   /* Check for a too small buffer.  */
10784b29b5fSFrançois Revol   if (n >= (ssize_t) len)
10884b29b5fSFrançois Revol     return -1;
10984b29b5fSFrançois Revol 
11084b29b5fSFrançois Revol   i = 0;
11184b29b5fSFrançois Revol   while (i < n && isdigit (buf[i]))
11284b29b5fSFrançois Revol     ++i;
11384b29b5fSFrançois Revol   *decpt = i;
11484b29b5fSFrançois Revol 
11584b29b5fSFrançois Revol   if (i == 0)
11684b29b5fSFrançois Revol     /* Value is Inf or NaN.  */
11784b29b5fSFrançois Revol     return 0;
11884b29b5fSFrançois Revol 
11984b29b5fSFrançois Revol   if (i < n)
12084b29b5fSFrançois Revol     {
12184b29b5fSFrançois Revol       do
12284b29b5fSFrançois Revol 	++i;
12384b29b5fSFrançois Revol       while (i < n && !isdigit (buf[i]));
12484b29b5fSFrançois Revol 
12584b29b5fSFrançois Revol       if (*decpt == 1 && buf[0] == '0' && value != 0.0)
12684b29b5fSFrançois Revol 	{
12784b29b5fSFrançois Revol 	  /* We must not have leading zeroes.  Strip them all out and
12884b29b5fSFrançois Revol 	     adjust *DECPT if necessary.  */
12984b29b5fSFrançois Revol 	  --*decpt;
13084b29b5fSFrançois Revol 	  while (i < n && buf[i] == '0')
13184b29b5fSFrançois Revol 	    {
13284b29b5fSFrançois Revol 	      --*decpt;
13384b29b5fSFrançois Revol 	      ++i;
13484b29b5fSFrançois Revol 	    }
13584b29b5fSFrançois Revol 	}
13684b29b5fSFrançois Revol 
13784b29b5fSFrançois Revol       memmove (&buf[MAX (*decpt, 0)], &buf[i], n - i);
13884b29b5fSFrançois Revol       buf[n - (i - MAX (*decpt, 0))] = '\0';
13984b29b5fSFrançois Revol     }
14084b29b5fSFrançois Revol 
14184b29b5fSFrançois Revol   if (left)
14284b29b5fSFrançois Revol     {
14384b29b5fSFrançois Revol       *decpt += left;
14484b29b5fSFrançois Revol       if ((ssize_t) --len > n)
14584b29b5fSFrançois Revol 	{
14684b29b5fSFrançois Revol 	  while (left-- > 0 && n < (ssize_t) len)
14784b29b5fSFrançois Revol 	    buf[n++] = '0';
14884b29b5fSFrançois Revol 	  buf[n] = '\0';
14984b29b5fSFrançois Revol 	}
15084b29b5fSFrançois Revol     }
15184b29b5fSFrançois Revol 
15284b29b5fSFrançois Revol   return 0;
15384b29b5fSFrançois Revol }
15484b29b5fSFrançois Revol libc_hidden_def (APPEND (FUNC_PREFIX, fcvt_r))
15584b29b5fSFrançois Revol 
15684b29b5fSFrançois Revol int
15784b29b5fSFrançois Revol APPEND (FUNC_PREFIX, ecvt_r) (value, ndigit, decpt, sign, buf, len)
15884b29b5fSFrançois Revol      FLOAT_TYPE value;
15984b29b5fSFrançois Revol      int ndigit, *decpt, *sign;
16084b29b5fSFrançois Revol      char *buf;
16184b29b5fSFrançois Revol      size_t len;
16284b29b5fSFrançois Revol {
16384b29b5fSFrançois Revol   int exponent = 0;
16484b29b5fSFrançois Revol 
16584b29b5fSFrançois Revol   if (isfinite (value) && value != 0.0)
16684b29b5fSFrançois Revol     {
16784b29b5fSFrançois Revol       /* Slow code that doesn't require -lm functions.  */
16884b29b5fSFrançois Revol       FLOAT_TYPE d;
16984b29b5fSFrançois Revol       FLOAT_TYPE f = 1.0;
17084b29b5fSFrançois Revol       if (value < 0.0)
17184b29b5fSFrançois Revol 	d = -value;
17284b29b5fSFrançois Revol       else
17384b29b5fSFrançois Revol 	d = value;
17484b29b5fSFrançois Revol       if (d < 1.0)
17584b29b5fSFrançois Revol 	{
17684b29b5fSFrançois Revol 	  do
17784b29b5fSFrançois Revol 	    {
17884b29b5fSFrançois Revol 	      f *= 10.0;
17984b29b5fSFrançois Revol 	      --exponent;
18084b29b5fSFrançois Revol 	    }
18184b29b5fSFrançois Revol 	  while (d * f < 1.0);
18284b29b5fSFrançois Revol 
18384b29b5fSFrançois Revol 	  value *= f;
18484b29b5fSFrançois Revol 	}
18584b29b5fSFrançois Revol       else if (d >= 10.0)
18684b29b5fSFrançois Revol 	{
18784b29b5fSFrançois Revol 	  do
18884b29b5fSFrançois Revol 	    {
18984b29b5fSFrançois Revol 	      f *= 10;
19084b29b5fSFrançois Revol 	      ++exponent;
19184b29b5fSFrançois Revol 	    }
19284b29b5fSFrançois Revol 	  while (d >= f * 10.0);
19384b29b5fSFrançois Revol 
19484b29b5fSFrançois Revol 	  value /= f;
19584b29b5fSFrançois Revol 	}
19684b29b5fSFrançois Revol     }
19784b29b5fSFrançois Revol   else if (value == 0.0)
19884b29b5fSFrançois Revol     /* SUSv2 leaves it unspecified whether *DECPT is 0 or 1 for 0.0.
19984b29b5fSFrançois Revol        This could be changed to -1 if we want to return 0.  */
20084b29b5fSFrançois Revol     exponent = 0;
20184b29b5fSFrançois Revol 
20284b29b5fSFrançois Revol   if (ndigit <= 0 && len > 0)
20384b29b5fSFrançois Revol     {
20484b29b5fSFrançois Revol       buf[0] = '\0';
20584b29b5fSFrançois Revol       *decpt = 1;
20684b29b5fSFrançois Revol       *sign = isfinite (value) ? signbit (value) != 0 : 0;
20784b29b5fSFrançois Revol     }
20884b29b5fSFrançois Revol   else
20984b29b5fSFrançois Revol     if (APPEND (FUNC_PREFIX, fcvt_r) (value, MIN (ndigit, NDIGIT_MAX) - 1,
21084b29b5fSFrançois Revol 				      decpt, sign, buf, len))
21184b29b5fSFrançois Revol       return -1;
21284b29b5fSFrançois Revol 
21384b29b5fSFrançois Revol   *decpt += exponent;
21484b29b5fSFrançois Revol   return 0;
21584b29b5fSFrançois Revol }
21684b29b5fSFrançois Revol libc_hidden_def (APPEND (FUNC_PREFIX, ecvt_r))
217