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