xref: /openbsd-src/lib/libc/stdlib/gcvt.c (revision cf2525843d483a385de106a1361b2b9c18d96583)
1 /*	$OpenBSD: gcvt.c,v 1.9 2006/01/10 16:18:37 millert 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 
30 char *
31 gcvt(double value, int ndigit, char *buf)
32 {
33 	char *digits, *dst, *src;
34 	int i, decpt, sign;
35 	struct lconv *lconv;
36 
37 	lconv = localeconv();
38 	if (ndigit == 0) {
39 		buf[0] = '\0';
40 		return (buf);
41 	}
42 
43 	digits = __dtoa(value, 2, ndigit, &decpt, &sign, NULL);
44 	if (decpt == 9999) {
45 		/*
46 		 * Infinity or NaN, convert to inf or nan with sign.
47 		 * We assume the buffer is at least ndigit long.
48 		 */
49 		snprintf(buf, ndigit + 1, "%s%s", sign ? "-" : "",
50 		    *digits == 'I' ? "inf" : "nan");
51 		return (buf);
52 	}
53 
54 	dst = buf;
55 	if (sign)
56 		*dst++ = '-';
57 
58 	if (decpt < 0 || decpt > ndigit) {
59 		/* exponential format (e.g. 1.2345e+13) */
60 		if (--decpt < 0) {
61 			sign = 1;
62 			decpt = -decpt;
63 		} else
64 			sign = 0;
65 		src = digits;
66 		*dst++ = *src++;
67 		*dst++ = *lconv->decimal_point;
68 		while (*src != '\0')
69 			*dst++ = *src++;
70 		*dst++ = 'e';
71 		if (sign)
72 			*dst++ = '-';
73 		else
74 			*dst++ = '+';
75 		if (decpt < 10) {
76 			*dst++ = '0';
77 			*dst++ = '0' + decpt;
78 			*dst = '\0';
79 		} else {
80 			/* XXX - optimize */
81 			for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
82 				continue;
83 			dst[i + 1] = '\0';
84 			while (decpt != 0) {
85 				dst[i--] = '0' + decpt % 10;
86 				decpt /= 10;
87 			}
88 		}
89 	} else {
90 		/* standard format */
91 		for (i = 0, src = digits; i < decpt; i++) {
92 			if (*src != '\0')
93 				*dst++ = *src++;
94 			else
95 				*dst++ = '0';
96 		}
97 		if (*src != '\0') {
98 			if (src == digits)
99 				*dst++ = '0';	/* zero before decimal point */
100 			*dst++ = *lconv->decimal_point;
101 			for (i = decpt; digits[i] != '\0'; i++) {
102 				*dst++ = digits[i];
103 			}
104 		}
105 		*dst = '\0';
106 	}
107 	return (buf);
108 }
109