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