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