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#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include "lint.h"
30#include "base_conversion.h"
31#include <sys/types.h>
32#include "libc.h"
33
34char *
35fconvert(double arg, int ndigits, int *decpt, int *sign, char *buf)
36{
37	decimal_mode    dm;
38	decimal_record  dr;
39	fp_exception_field_type ef;
40	int		i;
41
42#if defined(__sparc)
43	dm.rd = _QgetRD();
44#elif defined(__i386) || defined(__amd64)
45	dm.rd = __xgetRD();
46#else
47#error Unknown architecture
48#endif
49	dm.df = fixed_form;	/* F format. */
50	if (ndigits <= -DECIMAL_STRING_LENGTH)
51		ndigits = -DECIMAL_STRING_LENGTH + 1;
52	else if (ndigits >= DECIMAL_STRING_LENGTH)
53		ndigits = DECIMAL_STRING_LENGTH - 1;
54	dm.ndigits = ndigits;	/* Number of digits after point. */
55	double_to_decimal(&arg, &dm, &dr, &ef);
56	*sign = dr.sign;
57	switch (dr.fpclass) {
58	case fp_normal:
59	case fp_subnormal:
60		*decpt = dr.exponent + dr.ndigits;
61		for (i = 0; i < dr.ndigits; i++)
62			buf[i] = dr.ds[i];
63		/*
64		 * Pad with zeroes if we didn't get all the digits
65		 * we asked for.
66		 */
67		if (ndigits > 0 && dr.exponent > -ndigits) {
68			while (i < dr.ndigits + dr.exponent + ndigits)
69				buf[i++] = '0';
70		}
71		buf[i] = 0;
72		break;
73	case fp_zero:
74		*decpt = 0;
75		buf[0] = '0';
76		for (i = 1; i < ndigits; i++)
77			buf[i] = '0';
78		buf[i] = 0;
79		break;
80	default:
81		*decpt = 0;
82		__infnanstring(dr.fpclass, ndigits, buf);
83		break;
84	}
85	return (buf);
86}
87
88char *
89sfconvert(single *arg, int ndigits, int *decpt, int *sign, char *buf)
90{
91	decimal_mode    dm;
92	decimal_record  dr;
93	fp_exception_field_type ef;
94	int		i;
95
96#if defined(__sparc)
97	dm.rd = _QgetRD();
98#elif defined(__i386) || defined(__amd64)
99	dm.rd = __xgetRD();
100#else
101#error Unknown architecture
102#endif
103	dm.df = fixed_form;	/* F format. */
104	if (ndigits <= -DECIMAL_STRING_LENGTH)
105		ndigits = -DECIMAL_STRING_LENGTH + 1;
106	else if (ndigits >= DECIMAL_STRING_LENGTH)
107		ndigits = DECIMAL_STRING_LENGTH - 1;
108	dm.ndigits = ndigits;	/* Number of digits after point. */
109	single_to_decimal(arg, &dm, &dr, &ef);
110	*sign = dr.sign;
111	switch (dr.fpclass) {
112	case fp_normal:
113	case fp_subnormal:
114		*decpt = dr.exponent + dr.ndigits;
115		for (i = 0; i < dr.ndigits; i++)
116			buf[i] = dr.ds[i];
117		/*
118		 * Pad with zeroes if we didn't get all the digits
119		 * we asked for.
120		 */
121		if (ndigits > 0 && dr.exponent > -ndigits) {
122			while (i < dr.ndigits + dr.exponent + ndigits)
123				buf[i++] = '0';
124		}
125		buf[i] = 0;
126		break;
127	case fp_zero:
128		*decpt = 0;
129		buf[0] = '0';
130		for (i = 1; i < ndigits; i++)
131			buf[i] = '0';
132		buf[i] = 0;
133		break;
134	default:
135		*decpt = 0;
136		__infnanstring(dr.fpclass, ndigits, buf);
137		break;
138	}
139	return (buf);
140}
141
142char *
143qfconvert(quadruple *arg, int ndigits, int *decpt, int *sign, char *buf)
144{
145	decimal_mode    dm;
146	decimal_record  dr;
147	fp_exception_field_type ef;
148	int		i;
149
150#if defined(__sparc)
151	dm.rd = _QgetRD();
152#elif defined(__i386) || defined(__amd64)
153	dm.rd = __xgetRD();
154#else
155#error Unknown architecture
156#endif
157	dm.df = fixed_form;	/* F format. */
158	if (ndigits <= -DECIMAL_STRING_LENGTH)
159		ndigits = -DECIMAL_STRING_LENGTH + 1;
160	else if (ndigits >= DECIMAL_STRING_LENGTH)
161		ndigits = DECIMAL_STRING_LENGTH - 1;
162	dm.ndigits = ndigits;	/* Number of digits after point. */
163#if defined(__sparc)
164	quadruple_to_decimal(arg, &dm, &dr, &ef);
165#elif defined(__i386) || defined(__amd64)
166	extended_to_decimal((extended *)arg, &dm, &dr, &ef);
167#else
168#error Unknown architecture
169#endif
170	*sign = dr.sign;
171	if (ef & (1 << fp_overflow)) {
172		/*
173		 * *_to_decimal raises overflow whenever dr.ds isn't large
174		 * enough to hold all the digits requested.  For float and
175		 * double, this can only happen when the requested format
176		 * would require trailing zeroes, in which case fconvert
177		 * and sfconvert just add them.  For long double, the arg-
178		 * ument might be large enough that even the nonzero digits
179		 * would overflow dr.ds, so we punt instead.  (We could
180		 * distinguish these two cases, but it doesn't seem worth
181		 * changing things now, particularly since no real appli-
182		 * cation prints floating point numbers to 500 digits.)
183		 */
184		*decpt = 0;
185		buf[0] = 0;
186		return (buf);
187	}
188	switch (dr.fpclass) {
189	case fp_normal:
190	case fp_subnormal:
191		*decpt = dr.exponent + dr.ndigits;
192		for (i = 0; i < dr.ndigits; i++)
193			buf[i] = dr.ds[i];
194		buf[i] = 0;
195		break;
196	case fp_zero:
197		*decpt = 0;
198		buf[0] = '0';
199		for (i = 1; i < ndigits; i++)
200			buf[i] = '0';
201		buf[i] = 0;
202		break;
203	default:
204		*decpt = 0;
205		__infnanstring(dr.fpclass, ndigits, buf);
206		break;
207	}
208	return (buf);
209}
210