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