xref: /illumos-gate/usr/src/lib/libc/port/fp/fconvert.c (revision 7c478bd9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #pragma weak fconvert = _fconvert
30 #pragma weak sfconvert = _sfconvert
31 #pragma weak qfconvert = _qfconvert
32 
33 #include "synonyms.h"
34 #include "base_conversion.h"
35 #include <sys/types.h>
36 #include "libc.h"
37 
38 char *
39 fconvert(double arg, int ndigits, int *decpt, int *sign, char *buf)
40 {
41 	decimal_mode    dm;
42 	decimal_record  dr;
43 	fp_exception_field_type ef;
44 	int		i;
45 
46 #if defined(__sparc)
47 	dm.rd = _QgetRD();
48 #elif defined(__i386) || defined(__amd64)
49 	dm.rd = __xgetRD();
50 #else
51 #error Unknown architecture
52 #endif
53 	dm.df = fixed_form;	/* F format. */
54 	if (ndigits <= -DECIMAL_STRING_LENGTH)
55 		ndigits = -DECIMAL_STRING_LENGTH + 1;
56 	else if (ndigits >= DECIMAL_STRING_LENGTH)
57 		ndigits = DECIMAL_STRING_LENGTH - 1;
58 	dm.ndigits = ndigits;	/* Number of digits after point. */
59 	double_to_decimal(&arg, &dm, &dr, &ef);
60 	*sign = dr.sign;
61 	switch (dr.fpclass) {
62 	case fp_normal:
63 	case fp_subnormal:
64 		*decpt = dr.exponent + dr.ndigits;
65 		for (i = 0; i < dr.ndigits; i++)
66 			buf[i] = dr.ds[i];
67 		/*
68 		 * Pad with zeroes if we didn't get all the digits
69 		 * we asked for.
70 		 */
71 		if (ndigits > 0 && dr.exponent > -ndigits) {
72 			while (i < dr.ndigits + dr.exponent + ndigits)
73 				buf[i++] = '0';
74 		}
75 		buf[i] = 0;
76 		break;
77 	case fp_zero:
78 		*decpt = 0;
79 		buf[0] = '0';
80 		for (i = 1; i < ndigits; i++)
81 			buf[i] = '0';
82 		buf[i] = 0;
83 		break;
84 	default:
85 		*decpt = 0;
86 		__infnanstring(dr.fpclass, ndigits, buf);
87 		break;
88 	}
89 	return (buf);
90 }
91 
92 char *
93 sfconvert(single *arg, int ndigits, int *decpt, int *sign, char *buf)
94 {
95 	decimal_mode    dm;
96 	decimal_record  dr;
97 	fp_exception_field_type ef;
98 	int		i;
99 
100 #if defined(__sparc)
101 	dm.rd = _QgetRD();
102 #elif defined(__i386) || defined(__amd64)
103 	dm.rd = __xgetRD();
104 #else
105 #error Unknown architecture
106 #endif
107 	dm.df = fixed_form;	/* F format. */
108 	if (ndigits <= -DECIMAL_STRING_LENGTH)
109 		ndigits = -DECIMAL_STRING_LENGTH + 1;
110 	else if (ndigits >= DECIMAL_STRING_LENGTH)
111 		ndigits = DECIMAL_STRING_LENGTH - 1;
112 	dm.ndigits = ndigits;	/* Number of digits after point. */
113 	single_to_decimal(arg, &dm, &dr, &ef);
114 	*sign = dr.sign;
115 	switch (dr.fpclass) {
116 	case fp_normal:
117 	case fp_subnormal:
118 		*decpt = dr.exponent + dr.ndigits;
119 		for (i = 0; i < dr.ndigits; i++)
120 			buf[i] = dr.ds[i];
121 		/*
122 		 * Pad with zeroes if we didn't get all the digits
123 		 * we asked for.
124 		 */
125 		if (ndigits > 0 && dr.exponent > -ndigits) {
126 			while (i < dr.ndigits + dr.exponent + ndigits)
127 				buf[i++] = '0';
128 		}
129 		buf[i] = 0;
130 		break;
131 	case fp_zero:
132 		*decpt = 0;
133 		buf[0] = '0';
134 		for (i = 1; i < ndigits; i++)
135 			buf[i] = '0';
136 		buf[i] = 0;
137 		break;
138 	default:
139 		*decpt = 0;
140 		__infnanstring(dr.fpclass, ndigits, buf);
141 		break;
142 	}
143 	return (buf);
144 }
145 
146 char *
147 qfconvert(quadruple *arg, int ndigits, int *decpt, int *sign, char *buf)
148 {
149 	decimal_mode    dm;
150 	decimal_record  dr;
151 	fp_exception_field_type ef;
152 	int		i;
153 
154 #if defined(__sparc)
155 	dm.rd = _QgetRD();
156 #elif defined(__i386) || defined(__amd64)
157 	dm.rd = __xgetRD();
158 #else
159 #error Unknown architecture
160 #endif
161 	dm.df = fixed_form;	/* F format. */
162 	if (ndigits <= -DECIMAL_STRING_LENGTH)
163 		ndigits = -DECIMAL_STRING_LENGTH + 1;
164 	else if (ndigits >= DECIMAL_STRING_LENGTH)
165 		ndigits = DECIMAL_STRING_LENGTH - 1;
166 	dm.ndigits = ndigits;	/* Number of digits after point. */
167 #if defined(__sparc)
168 	quadruple_to_decimal(arg, &dm, &dr, &ef);
169 #elif defined(__i386) || defined(__amd64)
170 	extended_to_decimal((extended *)arg, &dm, &dr, &ef);
171 #else
172 #error Unknown architecture
173 #endif
174 	*sign = dr.sign;
175 	if (ef & (1 << fp_overflow)) {
176 		/*
177 		 * *_to_decimal raises overflow whenever dr.ds isn't large
178 		 * enough to hold all the digits requested.  For float and
179 		 * double, this can only happen when the requested format
180 		 * would require trailing zeroes, in which case fconvert
181 		 * and sfconvert just add them.  For long double, the arg-
182 		 * ument might be large enough that even the nonzero digits
183 		 * would overflow dr.ds, so we punt instead.  (We could
184 		 * distinguish these two cases, but it doesn't seem worth
185 		 * changing things now, particularly since no real appli-
186 		 * cation prints floating point numbers to 500 digits.)
187 		 */
188 		*decpt = 0;
189 		buf[0] = 0;
190 		return (buf);
191 	}
192 	switch (dr.fpclass) {
193 	case fp_normal:
194 	case fp_subnormal:
195 		*decpt = dr.exponent + dr.ndigits;
196 		for (i = 0; i < dr.ndigits; i++)
197 			buf[i] = dr.ds[i];
198 		buf[i] = 0;
199 		break;
200 	case fp_zero:
201 		*decpt = 0;
202 		buf[0] = '0';
203 		for (i = 1; i < ndigits; i++)
204 			buf[i] = '0';
205 		buf[i] = 0;
206 		break;
207 	default:
208 		*decpt = 0;
209 		__infnanstring(dr.fpclass, ndigits, buf);
210 		break;
211 	}
212 	return (buf);
213 }
214