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