xref: /dragonfly/lib/libc/stdio/xprintf_float.c (revision 0085a56d)
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
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
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
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