ftoastr: new module, for lossless conversion of floats to short strings
[pspp] / lib / ftoastr.c
1 /* floating point to accurate string
2
3    Copyright (C) 2010 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 <http://www.gnu.org/licenses/>.  */
17
18 /* Written by Paul Eggert.  */
19
20 #include "ftoastr.h"
21
22 #include "intprops.h"
23 #include <float.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #if LENGTH == 3
28 # define FLOAT long double
29 # define FLOAT_DIG LDBL_DIG
30 # define FLOAT_MIN LDBL_MIN
31 # define FLOAT_PREC_BOUND _GL_LDBL_PREC_BOUND
32 # define FTOASTR ldtoastr
33 # define STRTOF strtold
34 #elif LENGTH == 2
35 # define FLOAT double
36 # define FLOAT_DIG DBL_DIG
37 # define FLOAT_MIN DBL_MIN
38 # define FLOAT_PREC_BOUND _GL_DBL_PREC_BOUND
39 # define FTOASTR dtoastr
40 # define STRTOF strtod
41 #else
42 # define LENGTH 1
43 # define FLOAT float
44 # define FLOAT_DIG FLT_DIG
45 # define FLOAT_MIN FLT_MIN
46 # define FLOAT_PREC_BOUND _GL_FLT_PREC_BOUND
47 # define FTOASTR ftoastr
48 # define STRTOF strtof
49 #endif
50
51 int
52 FTOASTR (char *buf, size_t bufsize, int flags, int width, FLOAT x)
53 {
54   /* The following method is simple but slow.
55      For ideas about speeding things up, please see:
56
57      Florian Loitsch, Printing floating-point numbers quickly and accurately
58      with integers.  ACM SIGPLAN notices 46, 6 (June 2010), 233-243
59      <http://dx.doi.org/10.1145/1809028.1806623>.  */
60
61   char format[sizeof "%-+ 0*.*Lg"];
62   FLOAT abs_x = x < 0 ? -x : x;
63   int prec;
64
65   char *p = format;
66   *p++ = '%';
67
68   /* Support flags that generate output parsable by strtof.  */
69   *p = '-'; p += (flags & FTOASTR_LEFT_JUSTIFY  ) != 0;
70   *p = '+'; p += (flags & FTOASTR_ALWAYS_SIGNED ) != 0;
71   *p = ' '; p += (flags & FTOASTR_SPACE_POSITIVE) != 0;
72   *p = '0'; p += (flags & FTOASTR_ZERO_PAD      ) != 0;
73
74   *p++ = '*';
75   *p++ = '.';
76   *p++ = '*';
77   *p = 'L'; p += 2 < LENGTH;
78   *p++ = flags & FTOASTR_UPPER_E ? 'G' : 'g';
79   *p = '\0';
80
81   for (prec = abs_x < FLOAT_MIN ? 1 : FLOAT_DIG; ; prec++)
82     {
83       int n = snprintf (buf, bufsize, format, width, prec, x);
84       if (n < 0
85           || FLOAT_PREC_BOUND <= prec
86           || (n < bufsize && STRTOF (buf, NULL) == x))
87         return n;
88     }
89 }