root/lib/ftoastr.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ftoastr_snprintf
  2. FTOASTR

     1 /* floating point to accurate string
     2 
     3    Copyright (C) 2010-2023 Free Software Foundation, Inc.
     4 
     5    This program is free software: you can redistribute it and/or modify
     6    it under the terms of the GNU General Public License as published by
     7    the Free Software Foundation, either version 3 of the License, or
     8    (at your option) any later version.
     9 
    10    This program is distributed in the hope that it will be useful,
    11    but WITHOUT ANY WARRANTY; without even the implied warranty of
    12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13    GNU General Public License for more details.
    14 
    15    You should have received a copy of the GNU General Public License
    16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
    17 
    18 /* Written by Paul Eggert.  */
    19 
    20 /* This code can misbehave on some buggy or older platforms, when
    21    operating on arguments on floating types other than 'double', or
    22    when given unusual combinations of options.  Gnulib's
    23    snprintf-posix module works around many of these problems.
    24 
    25    This code relies on sprintf, strtod, etc. operating accurately;
    26    otherwise, the resulting strings could be inaccurate or too long.  */
    27 
    28 #include <config.h>
    29 
    30 #include "ftoastr.h"
    31 
    32 #include <float.h>
    33 #include <stdio.h>
    34 #include <stdlib.h>
    35 
    36 #ifdef C_LOCALE
    37 # include "c-snprintf.h"
    38 # include "c-strtod.h"
    39 # define PREFIX(name) c_ ## name
    40 #else
    41 # define PREFIX(name) name
    42 #endif
    43 
    44 #if LENGTH == 3
    45 # define FLOAT long double
    46 # define FLOAT_DIG LDBL_DIG
    47 # define FLOAT_MIN LDBL_MIN
    48 # define FLOAT_PREC_BOUND _GL_LDBL_PREC_BOUND
    49 # define FTOASTR PREFIX (ldtoastr)
    50 # define PROMOTED_FLOAT long double
    51 # define STRTOF PREFIX (strtold)
    52 #elif LENGTH == 2
    53 # define FLOAT double
    54 # define FLOAT_DIG DBL_DIG
    55 # define FLOAT_MIN DBL_MIN
    56 # define FLOAT_PREC_BOUND _GL_DBL_PREC_BOUND
    57 # define FTOASTR PREFIX (dtoastr)
    58 # define PROMOTED_FLOAT double
    59 #else
    60 # define LENGTH 1
    61 # define FLOAT float
    62 # define FLOAT_DIG FLT_DIG
    63 # define FLOAT_MIN FLT_MIN
    64 # define FLOAT_PREC_BOUND _GL_FLT_PREC_BOUND
    65 # define FTOASTR PREFIX (ftoastr)
    66 # define PROMOTED_FLOAT double
    67 # if HAVE_STRTOF
    68 #  define STRTOF strtof
    69 # endif
    70 #endif
    71 
    72 /* On pre-C99 hosts, approximate strtof with strtod.  This
    73    may generate one or two extra digits, but that's better than not
    74    working at all.  */
    75 #ifndef STRTOF
    76 # define STRTOF PREFIX (strtod)
    77 #endif
    78 
    79 /* On hosts where it's not known that snprintf works, use sprintf to
    80    implement the subset needed here.  Typically BUFSIZE is big enough
    81    and there's little or no performance hit.  */
    82 #ifdef C_LOCALE
    83 # undef snprintf
    84 # define snprintf c_snprintf
    85 #elif ! GNULIB_SNPRINTF
    86 # undef snprintf
    87 # define snprintf ftoastr_snprintf
    88 static int
    89 ftoastr_snprintf (char *buf, size_t bufsize, char const *format,
    90                   int width, int prec, FLOAT x)
    91 {
    92   PROMOTED_FLOAT promoted_x = x;
    93   char width_0_buffer[LENGTH == 1 ? FLT_BUFSIZE_BOUND
    94                       : LENGTH == 2 ? DBL_BUFSIZE_BOUND
    95                       : LDBL_BUFSIZE_BOUND];
    96   int n = width;
    97   if (bufsize < sizeof width_0_buffer)
    98     {
    99       n = sprintf (width_0_buffer, format, 0, prec, promoted_x);
   100       if (n < 0)
   101         return n;
   102       if (n < width)
   103         n = width;
   104     }
   105   if (n < bufsize)
   106     n = sprintf (buf, format, width, prec, promoted_x);
   107   return n;
   108 }
   109 #endif
   110 
   111 int
   112 FTOASTR (char *buf, size_t bufsize, int flags, int width, FLOAT x)
   113 {
   114   /* The following method is simple but slow.
   115      For ideas about speeding things up, please see:
   116 
   117      Andrysco M, Jhala R, Lerner S. Printing floating-point numbers:
   118      a faster, always correct method. ACM SIGPLAN notices - POPL '16.
   119      2016;51(1):555-67 <https://doi.org/10.1145/2914770.2837654>; draft at
   120      <https://cseweb.ucsd.edu/~lerner/papers/fp-printing-popl16.pdf>.  */
   121 
   122   PROMOTED_FLOAT promoted_x = x;
   123   char format[sizeof "%-+ 0*.*Lg"];
   124   FLOAT abs_x = x < 0 ? -x : x;
   125   int prec;
   126 
   127   char *p = format;
   128   *p++ = '%';
   129 
   130   /* Support flags that generate output parsable by strtof.  */
   131   *p = '-'; p += (flags & FTOASTR_LEFT_JUSTIFY  ) != 0;
   132   *p = '+'; p += (flags & FTOASTR_ALWAYS_SIGNED ) != 0;
   133   *p = ' '; p += (flags & FTOASTR_SPACE_POSITIVE) != 0;
   134   *p = '0'; p += (flags & FTOASTR_ZERO_PAD      ) != 0;
   135 
   136   *p++ = '*';
   137   *p++ = '.';
   138   *p++ = '*';
   139   *p = 'L'; p += 2 < LENGTH;
   140   *p++ = flags & FTOASTR_UPPER_E ? 'G' : 'g';
   141   *p = '\0';
   142 
   143   for (prec = abs_x < FLOAT_MIN ? 1 : FLOAT_DIG; ; prec++)
   144     {
   145       int n = snprintf (buf, bufsize, format, width, prec, promoted_x);
   146       if (n < 0
   147           || FLOAT_PREC_BOUND <= prec
   148           || (n < bufsize && STRTOF (buf, NULL) == x))
   149         return n;
   150     }
   151 }

/* [<][>][^][v][top][bottom][index][help] */