1 /*	$OpenBSD: gcvt.c,v 1.14 2019/01/25 00:19:25 millert Exp $	*/
2 
3 /*
4  * Copyright (c) 2002, 2003, 2006, 2010
5  *	Todd C. Miller <millert@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  *
19  * Sponsored in part by the Defense Advanced Research Projects
20  * Agency (DARPA) and Air Force Research Laboratory, Air Force
21  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22  */
23 
24 #include <locale.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "gdtoa.h"
29 
30 #define DEFPREC	6
31 
32 char *
gcvt(double value,int ndigit,char * buf)33 gcvt(double value, int ndigit, char *buf)
34 {
35 	char *digits, *dst, *src;
36 	int i, decpt, sign;
37 	struct lconv *lconv;
38 
39 	lconv = localeconv();
40 	if (ndigit <= 0) {
41 		/* Match printf(3) behavior. */
42 		ndigit = ndigit ? DEFPREC : 1;
43 	}
44 
45 	digits = __dtoa(value, 2, ndigit, &decpt, &sign, NULL);
46 	if (digits == NULL)
47 		return (NULL);
48 	if (decpt == 9999) {
49 		/*
50 		 * Infinity or NaN, convert to inf or nan with sign.
51 		 * We can't infer buffer size based on ndigit.
52 		 * We have to assume it is at least 5 chars.
53 		 */
54 		snprintf(buf, 5, "%s%s", sign ? "-" : "",
55 		    *digits == 'I' ? "inf" : "nan");
56 		__freedtoa(digits);
57 		return (buf);
58 	}
59 
60 	dst = buf;
61 	if (sign)
62 		*dst++ = '-';
63 
64 	/* Match printf(3) behavior for exponential vs. regular fomatting. */
65 	if (decpt <= -4 || decpt > ndigit) {
66 		/* exponential format (e.g. 1.2345e+13) */
67 		if (--decpt < 0) {
68 			sign = 1;
69 			decpt = -decpt;
70 		} else
71 			sign = 0;
72 		src = digits;
73 		*dst++ = *src++;
74 		if (*src != '\0') {
75 			*dst++ = *lconv->decimal_point;
76 			do {
77 				*dst++ = *src++;
78 			} while (*src != '\0');
79 		}
80 		*dst++ = 'e';
81 		if (sign)
82 			*dst++ = '-';
83 		else
84 			*dst++ = '+';
85 		if (decpt < 10) {
86 			*dst++ = '0';
87 			*dst++ = '0' + decpt;
88 			*dst = '\0';
89 		} else {
90 			/* XXX - optimize */
91 			for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
92 				continue;
93 			dst[i + 1] = '\0';
94 			while (decpt != 0) {
95 				dst[i--] = '0' + decpt % 10;
96 				decpt /= 10;
97 			}
98 		}
99 	} else {
100 		/* standard format */
101 		for (i = 0, src = digits; i < decpt; i++) {
102 			if (*src != '\0')
103 				*dst++ = *src++;
104 			else
105 				*dst++ = '0';
106 		}
107 		if (*src != '\0') {
108 			if (src == digits)
109 				*dst++ = '0';	/* zero before decimal point */
110 			*dst++ = *lconv->decimal_point;
111 			while (decpt < 0) {
112 				*dst++ = '0';
113 				decpt++;
114 			}
115 			for (i = decpt; digits[i] != '\0'; i++) {
116 				*dst++ = digits[i];
117 			}
118 		}
119 		*dst = '\0';
120 	}
121 	__freedtoa(digits);
122 	return (buf);
123 }
124