xref: /dragonfly/lib/libc/stdio/xprintf_float.c (revision cf515c3a6f3a8964ad592e524442bc628f8ed63b)
1 /*-
2  * Copyright (c) 2005 Poul-Henning Kamp
3  * Copyright (c) 1990, 1993
4  *        The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Chris Torek.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD: src/lib/libc/stdio/xprintf_float.c,v 1.1 2005/12/16 18:56:38 phk Exp $
34  */
35 
36 #include "namespace.h"
37 #include <stdio.h>
38 #include <wchar.h>
39 #include <assert.h>
40 #include <locale.h>
41 #include <limits.h>
42 
43 #define   dtoa                __dtoa
44 #define   freedtoa  __freedtoa
45 
46 #include <float.h>
47 #include <math.h>
48 #include "un-namespace.h"
49 #include "gdtoa.h"
50 #include "floatio.h"
51 #include "printf.h"
52 
53 /*
54  * The size of the buffer we use as scratch space for integer
55  * conversions, among other things.  Technically, we would need the
56  * most space for base 10 conversions with thousands' grouping
57  * characters between each pair of digits.  100 bytes is a
58  * conservative overestimate even for a 128-bit uintmax_t.
59  */
60 #define   BUF       100
61 
62 #define   DEFPREC             6         /* Default FP precision */
63 
64 
65 /* various globals ---------------------------------------------------*/
66 
67 
68 /* padding function---------------------------------------------------*/
69 
70 #define   PRINTANDPAD(p, ep, len, with) do {                \
71           n2 = (ep) - (p);                                            \
72           if (n2 > (len))                                             \
73                     n2 = (len);                                       \
74           if (n2 > 0)                                                 \
75                     ret += __printf_puts(io, (p), n2);                \
76           ret += __printf_pad(io, (len) - (n2 > 0 ? n2 : 0), (with)); \
77 } while(0)
78 
79 /* misc --------------------------------------------------------------*/
80 
81 #define   to_char(n)          ((n) + '0')
82 
83 static int
exponent(char * p0,int expo,int fmtch)84 exponent(char *p0, int expo, int fmtch)
85 {
86           char *p, *t;
87           char expbuf[MAXEXPDIG];
88 
89           p = p0;
90           *p++ = fmtch;
91           if (expo < 0) {
92                     expo = -expo;
93                     *p++ = '-';
94           } else {
95                     *p++ = '+';
96           }
97           t = expbuf + MAXEXPDIG;
98           if (expo > 9) {
99                     do {
100                               *--t = to_char(expo % 10);
101                     } while ((expo /= 10) > 9);
102                     *--t = to_char(expo);
103                     for (; t < expbuf + MAXEXPDIG; *p++ = *t++)
104                               ;
105           } else {
106                     /*
107                      * Exponents for decimal floating point conversions
108                      * (%[eEgG]) must be at least two characters long,
109                      * whereas exponents for hexadecimal conversions can
110                      * be only one character long.
111                      */
112                     if (fmtch == 'e' || fmtch == 'E')
113                               *p++ = '0';
114                     *p++ = to_char(expo);
115           }
116           return (p - p0);
117 }
118 
119 /* 'f' ---------------------------------------------------------------*/
120 
121 int
__printf_arginfo_float(const struct printf_info * pi,size_t n,int * argt)122 __printf_arginfo_float(const struct printf_info *pi, size_t n, int *argt)
123 {
124           assert (n > 0);
125           argt[0] = PA_DOUBLE;
126           if (pi->is_long_double)
127                     argt[0] |= PA_FLAG_LONG_DOUBLE;
128           return (1);
129 }
130 
131 /*
132  * We can decompose the printed representation of floating
133  * point numbers into several parts, some of which may be empty:
134  *
135  * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
136  *    A       B     ---C---      D       E   F
137  *
138  * A:     'sign' holds this value if present; '\0' otherwise
139  * B:     ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
140  * C:     cp points to the string MMMNNN.  Leading and trailing
141  *        zeroes are not in the string and must be added.
142  * D:     expchar holds this character; '\0' if no exponent, e.g. %f
143  * F:     at least two digits for decimal, at least one digit for hex
144  */
145 
146 int
__printf_render_float(struct __printf_io * io,const struct printf_info * pi,const void * const * arg)147 __printf_render_float(struct __printf_io *io, const struct printf_info *pi,
148                           const void *const *arg)
149 {
150           int prec;           /* precision from format; <0 for N/A */
151           char *dtoaresult;   /* buffer allocated by dtoa */
152           char expchar;                 /* exponent character: [eEpP\0] */
153           char *cp;
154           int expt;           /* integer value of exponent */
155           int signflag;                 /* true if float is negative */
156           char *dtoaend;                /* pointer to end of converted digits */
157           char sign;                    /* sign prefix (' ', '+', '-', or \0) */
158           int size;           /* size of converted field or string */
159           int ndig;           /* actual number of digits returned by dtoa */
160           int expsize;                  /* character count for expstr */
161           char expstr[MAXEXPDIG+2];     /* buffer for exponent string: e+ZZZ */
162           int nseps;                    /* number of group separators with ' */
163           int nrepeats;                 /* number of repeats of the last group */
164           const char *grouping;         /* locale specific numeric grouping rules */
165           int lead;           /* sig figs before decimal or group sep */
166           long double ld;
167           double d;
168           int realsz;                   /* field size expanded by dprec, sign, etc */
169           int dprec;                    /* a copy of prec if [diouxX], 0 otherwise */
170           char ox[2];                   /* space for 0x; ox[1] is either x, X, or \0 */
171           int ret;            /* return value accumulator */
172           char *decimal_point;          /* locale specific decimal point */
173           int n2;                       /* XXX: for PRINTANDPAD */
174           char thousands_sep; /* locale specific thousands separator */
175           char buf[BUF];                /* buffer with space for digits of uintmax_t */
176           const char *xdigs;
177           int flag;
178 
179           prec = pi->prec;
180           ox[1] = '\0';
181           sign = pi->showsign;
182           flag = 0;
183           ret = 0;
184 
185           thousands_sep = *(localeconv()->thousands_sep);
186           grouping = NULL;
187           if (pi->alt)
188                     grouping = localeconv()->grouping;
189           decimal_point = localeconv()->decimal_point;
190           dprec = -1;
191 
192           switch(pi->spec) {
193           case 'a':
194           case 'A':
195                     if (pi->spec == 'a') {
196                               ox[1] = 'x';
197                               xdigs = __lowercase_hex;
198                               expchar = 'p';
199                     } else {
200                               ox[1] = 'X';
201                               xdigs = __uppercase_hex;
202                               expchar = 'P';
203                     }
204                     if (prec >= 0)
205                               prec++;
206                     if (pi->is_long_double) {
207                               ld = *((long double *)arg[0]);
208                               dtoaresult = cp =
209                                   __hldtoa(ld, xdigs, prec,
210                                   &expt, &signflag, &dtoaend);
211                     } else {
212                               d = *((double *)arg[0]);
213                               dtoaresult = cp =
214                                   __hdtoa(d, xdigs, prec,
215                                   &expt, &signflag, &dtoaend);
216                     }
217                     if (prec < 0)
218                               prec = dtoaend - cp;
219                     if (expt == INT_MAX)
220                               ox[1] = '\0';
221                     goto fp_common;
222           case 'e':
223           case 'E':
224                     expchar = pi->spec;
225                     if (prec < 0)       /* account for digit before decpt */
226                               prec = DEFPREC + 1;
227                     else
228                               prec++;
229                     break;
230           case 'f':
231           case 'F':
232                     expchar = '\0';
233                     break;
234           case 'g':
235           case 'G':
236                     expchar = pi->spec - ('g' - 'e');
237                     if (prec == 0)
238                               prec = 1;
239                     break;
240           default:
241                     assert(pi->spec == 'f');
242           }
243 
244           if (prec < 0)
245                     prec = DEFPREC;
246           if (pi->is_long_double) {
247                     ld = *((long double *)arg[0]);
248                     dtoaresult = cp =
249                         __ldtoa(&ld, expchar ? 2 : 3, prec,
250                         &expt, &signflag, &dtoaend);
251           } else {
252                     d = *((double *)arg[0]);
253                     dtoaresult = cp =
254                         dtoa(d, expchar ? 2 : 3, prec,
255                         &expt, &signflag, &dtoaend);
256                     if (expt == 9999)
257                               expt = INT_MAX;
258           }
259 fp_common:
260           if (signflag)
261                     sign = '-';
262           if (expt == INT_MAX) {        /* inf or nan */
263                     if (*cp == 'N') {
264                               cp = (pi->spec >= 'a') ? "nan" : "NAN";
265                               sign = '\0';
266                     } else {
267                               cp = (pi->spec >= 'a') ? "inf" : "INF";
268                     }
269                     size = 3;
270                     flag = 1;
271                     goto here;
272           }
273           ndig = dtoaend - cp;
274           if (pi->spec == 'g' || pi->spec == 'G') {
275                     if (expt > -4 && expt <= prec) {
276                               /* Make %[gG] smell like %[fF] */
277                               expchar = '\0';
278                               if (pi->alt)
279                                         prec -= expt;
280                               else
281                                         prec = ndig - expt;
282                               if (prec < 0)
283                                         prec = 0;
284                     } else {
285                               /*
286                                * Make %[gG] smell like %[eE], but
287                                * trim trailing zeroes if no # flag.
288                                */
289                               if (!pi->alt)
290                                         prec = ndig;
291                     }
292           }
293           if (expchar) {
294                     expsize = exponent(expstr, expt - 1, expchar);
295                     size = expsize + prec;
296                     if (prec > 1 || pi->alt)
297                               ++size;
298           } else {
299                     /* space for digits before decimal point */
300                     if (expt > 0)
301                               size = expt;
302                     else      /* "0" */
303                               size = 1;
304                     /* space for decimal pt and following digits */
305                     if (prec || pi->alt)
306                               size += prec + 1;
307                     if (grouping && expt > 0) {
308                               /* space for thousands' grouping */
309                               nseps = nrepeats = 0;
310                               lead = expt;
311                               while (*grouping != CHAR_MAX) {
312                                         if (lead <= *grouping)
313                                                   break;
314                                         lead -= *grouping;
315                                         if (*(grouping+1)) {
316                                                   nseps++;
317                                                   grouping++;
318                                         } else {
319                                                   nrepeats++;
320                                         }
321                               }
322                               size += nseps + nrepeats;
323                     } else {
324                               lead = expt;
325                     }
326           }
327 
328 here:
329           /*
330            * All reasonable formats wind up here.  At this point, `cp'
331            * points to a string which (if not flags&LADJUST) should be
332            * padded out to `width' places.  If flags&ZEROPAD, it should
333            * first be prefixed by any sign or other prefix; otherwise,
334            * it should be blank padded before the prefix is emitted.
335            * After any left-hand padding and prefixing, emit zeroes
336            * required by a decimal [diouxX] precision, then print the
337            * string proper, then emit zeroes required by any leftover
338            * floating precision; finally, if LADJUST, pad with blanks.
339            *
340            * Compute actual size, so we know how much to pad.
341            * size excludes decimal prec; realsz includes it.
342            */
343           realsz = dprec > size ? dprec : size;
344           if (sign)
345                     realsz++;
346           if (ox[1])
347                     realsz += 2;
348 
349           /* right-adjusting blank padding */
350           if (pi->pad != '0' && pi->left == 0)
351                     ret += __printf_pad(io, pi->width - realsz, 0);
352 
353           /* prefix */
354           if (sign)
355                     ret += __printf_puts(io, &sign, 1);
356 
357           if (ox[1]) {        /* ox[1] is either x, X, or \0 */
358                     ox[0] = '0';
359                     ret += __printf_puts(io, ox, 2);
360           }
361 
362           /* right-adjusting zero padding */
363           if (pi->pad == '0' && pi->left == 0)
364                     ret += __printf_pad(io, pi->width - realsz, 1);
365 
366           /* leading zeroes from decimal precision */
367           ret += __printf_pad(io, dprec - size, 1);
368 
369           if (flag) {
370                     ret += __printf_puts(io, cp, size);
371           } else {
372                     /* glue together f_p fragments */
373                     if (!expchar) {     /* %[fF] or sufficiently short %[gG] */
374                               if (expt <= 0) {
375                                         ret += __printf_puts(io, "0", 1);
376                                         if (prec || pi->alt)
377                                                   ret += __printf_puts(io, decimal_point, 1);
378                                         ret += __printf_pad(io, -expt, 1);
379                                         /* already handled initial 0's */
380                                         prec += expt;
381                               } else {
382                                         PRINTANDPAD(cp, dtoaend, lead, 1);
383                                         cp += lead;
384                                         if (grouping) {
385                                                   while (nseps>0 || nrepeats>0) {
386                                                             if (nrepeats > 0) {
387                                                                       nrepeats--;
388                                                             } else {
389                                                                       grouping--;
390                                                                       nseps--;
391                                                             }
392                                                             ret += __printf_puts(io, &thousands_sep, 1);
393                                                             PRINTANDPAD(cp,dtoaend,
394                                                                 *grouping, 1);
395                                                             cp += *grouping;
396                                                   }
397                                                   if (cp > dtoaend)
398                                                             cp = dtoaend;
399                                         }
400                                         if (prec || pi->alt)
401                                                   ret += __printf_puts(io, decimal_point,1);
402                               }
403                               PRINTANDPAD(cp, dtoaend, prec, 1);
404                     } else {  /* %[eE] or sufficiently long %[gG] */
405                               if (prec > 1 || pi->alt) {
406                                         buf[0] = *cp++;
407                                         buf[1] = *decimal_point;
408                                         ret += __printf_puts(io, buf, 2);
409                                         ret += __printf_puts(io, cp, ndig-1);
410                                         ret += __printf_pad(io, prec - ndig, 1);
411                               } else {  /* XeYYY */
412                                         ret += __printf_puts(io, cp, 1);
413                               }
414                               ret += __printf_puts(io, expstr, expsize);
415                     }
416           }
417           /* left-adjusting padding (always blank) */
418           if (pi->left)
419                     ret += __printf_pad(io, pi->width - realsz, 0);
420 
421           __printf_flush(io);
422           if (dtoaresult != NULL)
423                     freedtoa(dtoaresult);
424 
425           return (ret);
426 }
427