xref: /openbsd-src/lib/libc/stdlib/gcvt.c (revision 2c53affbcc0119d6480b86c18f2790523b6a0aad)
1*2c53affbSjmc /*	$OpenBSD: gcvt.c,v 1.15 2022/12/27 17:10:06 jmc Exp $	*/
280944f19Smillert 
380944f19Smillert /*
4260966efSmillert  * Copyright (c) 2002, 2003, 2006, 2010
5bf198cc6Smillert  *	Todd C. Miller <millert@openbsd.org>
680944f19Smillert  *
706f01696Smillert  * Permission to use, copy, modify, and distribute this software for any
806f01696Smillert  * purpose with or without fee is hereby granted, provided that the above
906f01696Smillert  * copyright notice and this permission notice appear in all copies.
1080944f19Smillert  *
11328f1f07Smillert  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12328f1f07Smillert  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13328f1f07Smillert  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14328f1f07Smillert  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15328f1f07Smillert  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16328f1f07Smillert  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17328f1f07Smillert  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18328f1f07Smillert  *
19328f1f07Smillert  * Sponsored in part by the Defense Advanced Research Projects
20328f1f07Smillert  * Agency (DARPA) and Air Force Research Laboratory, Air Force
21328f1f07Smillert  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
2280944f19Smillert  */
2380944f19Smillert 
2445b391e8Smillert #include <locale.h>
2580944f19Smillert #include <stdio.h>
2680944f19Smillert #include <stdlib.h>
2780944f19Smillert #include <string.h>
28def3c8f6Sguenther #include "gdtoa.h"
2980944f19Smillert 
30260966efSmillert #define DEFPREC	6
31260966efSmillert 
3280944f19Smillert char *
gcvt(double value,int ndigit,char * buf)3380944f19Smillert gcvt(double value, int ndigit, char *buf)
3480944f19Smillert {
3580944f19Smillert 	char *digits, *dst, *src;
3680944f19Smillert 	int i, decpt, sign;
3745b391e8Smillert 	struct lconv *lconv;
3880944f19Smillert 
3945b391e8Smillert 	lconv = localeconv();
40260966efSmillert 	if (ndigit <= 0) {
41260966efSmillert 		/* Match printf(3) behavior. */
42260966efSmillert 		ndigit = ndigit ? DEFPREC : 1;
4380944f19Smillert 	}
4480944f19Smillert 
4580944f19Smillert 	digits = __dtoa(value, 2, ndigit, &decpt, &sign, NULL);
46384cfdc1Smartynas 	if (digits == NULL)
47384cfdc1Smartynas 		return (NULL);
48aed36ee6Smillert 	if (decpt == 9999) {
493a59a6c9Smillert 		/*
503a59a6c9Smillert 		 * Infinity or NaN, convert to inf or nan with sign.
51260966efSmillert 		 * We can't infer buffer size based on ndigit.
52260966efSmillert 		 * We have to assume it is at least 5 chars.
533a59a6c9Smillert 		 */
54260966efSmillert 		snprintf(buf, 5, "%s%s", sign ? "-" : "",
553a59a6c9Smillert 		    *digits == 'I' ? "inf" : "nan");
5657536318Sderaadt 		__freedtoa(digits);
57aed36ee6Smillert 		return (buf);
58aed36ee6Smillert 	}
5980944f19Smillert 
6080944f19Smillert 	dst = buf;
6180944f19Smillert 	if (sign)
6280944f19Smillert 		*dst++ = '-';
6380944f19Smillert 
64*2c53affbSjmc 	/* Match printf(3) behavior for exponential vs. regular formatting. */
65260966efSmillert 	if (decpt <= -4 || decpt > ndigit) {
66df92a088Smillert 		/* exponential format (e.g. 1.2345e+13) */
6780944f19Smillert 		if (--decpt < 0) {
6880944f19Smillert 			sign = 1;
6980944f19Smillert 			decpt = -decpt;
7080944f19Smillert 		} else
7180944f19Smillert 			sign = 0;
72df92a088Smillert 		src = digits;
73df92a088Smillert 		*dst++ = *src++;
74260966efSmillert 		if (*src != '\0') {
7545b391e8Smillert 			*dst++ = *lconv->decimal_point;
76260966efSmillert 			do {
7780944f19Smillert 				*dst++ = *src++;
78260966efSmillert 			} while (*src != '\0');
79260966efSmillert 		}
8080944f19Smillert 		*dst++ = 'e';
8180944f19Smillert 		if (sign)
8280944f19Smillert 			*dst++ = '-';
8380944f19Smillert 		else
8480944f19Smillert 			*dst++ = '+';
8580944f19Smillert 		if (decpt < 10) {
8680944f19Smillert 			*dst++ = '0';
8780944f19Smillert 			*dst++ = '0' + decpt;
8880944f19Smillert 			*dst = '\0';
8980944f19Smillert 		} else {
9080944f19Smillert 			/* XXX - optimize */
9180944f19Smillert 			for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
92df92a088Smillert 				continue;
93df92a088Smillert 			dst[i + 1] = '\0';
9480944f19Smillert 			while (decpt != 0) {
9580944f19Smillert 				dst[i--] = '0' + decpt % 10;
9680944f19Smillert 				decpt /= 10;
9780944f19Smillert 			}
9880944f19Smillert 		}
9980944f19Smillert 	} else {
10080944f19Smillert 		/* standard format */
10180944f19Smillert 		for (i = 0, src = digits; i < decpt; i++) {
10280944f19Smillert 			if (*src != '\0')
10380944f19Smillert 				*dst++ = *src++;
10480944f19Smillert 			else
10580944f19Smillert 				*dst++ = '0';
10680944f19Smillert 		}
10780944f19Smillert 		if (*src != '\0') {
108df92a088Smillert 			if (src == digits)
109df92a088Smillert 				*dst++ = '0';	/* zero before decimal point */
11045b391e8Smillert 			*dst++ = *lconv->decimal_point;
111260966efSmillert 			while (decpt < 0) {
112260966efSmillert 				*dst++ = '0';
113260966efSmillert 				decpt++;
114260966efSmillert 			}
11580944f19Smillert 			for (i = decpt; digits[i] != '\0'; i++) {
11680944f19Smillert 				*dst++ = digits[i];
11780944f19Smillert 			}
11880944f19Smillert 		}
11980944f19Smillert 		*dst = '\0';
12080944f19Smillert 	}
12157536318Sderaadt 	__freedtoa(digits);
12280944f19Smillert 	return (buf);
12380944f19Smillert }
124