root/maint/gnulib/lib/ftoastr.c

/* [previous][next][first][last][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-2021 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,
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

/* [previous][next][first][last][top][bottom][index][help] */