xref: /titanic_44/usr/src/lib/libc/port/fp/aconvert.c (revision fcf3ce441efd61da9bb2884968af01cb7c1452cc)
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 
32 /* translation table from hex values to hex chars */
33 static const char *hexchar = "0123456789abcdef";
34 
35 /*
36  * Convert arg to a hexadecimal string.
37  *
38  * If arg is finite and nonzero, buf is filled with ndigits hexadecimal
39  * digits, representing the significand of arg, followed by a null byte
40  * (so ndigits must be at least 1 and buf must be large enough to hold
41  * ndigits + 1 characters).  If ndigits is large enough, the representa-
42  * tion is exact; otherwise, the value is rounded according to the pre-
43  * vailing rounding mode to fit the requested number of digits.  Either
44  * way, the result is normalized so that the first digit is '1'.  The
45  * corresponding base two exponent is passed back in *exp.
46  *
47  * If arg is zero, buf is filled with ndigits zeros followed by a null,
48  * and *exp is set to zero.  If arg is infinite or NaN, __infnanstring
49  * is called to place an appropriate string in buf, and *exp is set to
50  * zero.
51  *
52  * Regardless of the value of arg, its sign bit is stored in *sign.
53  */
54 
55 #if defined(__sparc)
56 
57 void
58 __aconvert(double arg, int ndigits, int *exp, int *sign, char *buf)
59 {
60 	union {
61 		unsigned int	i[2];
62 		long long	l;
63 		double		d;
64 	} a, c;
65 	int		ha, i, s;
66 	unsigned int	d;
67 
68 	a.d = arg;
69 	*sign = s = a.i[0] >> 31;
70 	ha = a.i[0] & ~0x80000000;
71 
72 	/* check for infinity or nan */
73 	if (ha >= 0x7ff00000) {
74 		*exp = 0;
75 		__infnanstring((ha == 0x7ff00000 && a.i[1] == 0)?
76 		    fp_infinity : fp_quiet, ndigits, buf);
77 		return;
78 	}
79 
80 	/* check for subnormal or zero */
81 	if (ha < 0x00100000) {
82 		if ((ha | a.i[1]) == 0) {
83 			*exp = 0;
84 			for (i = 0; i < ndigits; i++)
85 				buf[i] = '0';
86 			buf[ndigits] = '\0';
87 			return;
88 		}
89 
90 		/*
91 		 * Normalize.  It would be much simpler if we could just
92 		 * multiply by a power of two here, but some SPARC imple-
93 		 * mentations would flush the subnormal operand to zero
94 		 * when nonstandard mode is enabled.
95 		 */
96 		a.i[0] = ha;
97 		a.d = (double)a.l;
98 		if (s)
99 			a.d = -a.d;
100 		ha = a.i[0] & ~0x80000000;
101 		*exp = (ha >> 20) - 0x3ff - 1074;
102 	} else {
103 		*exp = (ha >> 20) - 0x3ff;
104 	}
105 
106 	if (ndigits < 14) {
107 		/*
108 		 * Round the significand at the appropriate bit by adding
109 		 * and subtracting a power of two.  This will also raise
110 		 * the inexact exception if anything is rounded off.
111 		 */
112 		c.i[0] = (0x43700000 | (s << 31)) - (ndigits << 22);
113 		c.i[1] = 0;
114 		a.i[0] = (a.i[0] & 0x800fffff) | 0x3ff00000;
115 		a.d = (a.d + c.d) - c.d;
116 		ha = a.i[0] & ~0x80000000;
117 		if (ha >= 0x40000000)
118 			(*exp)++;
119 	}
120 
121 	/* convert to hex digits */
122 	buf[0] = '1';
123 	d = ha << 12;
124 	for (i = 1; i < ndigits && i < 6; i++) {
125 		buf[i] = hexchar[d >> 28];
126 		d <<= 4;
127 	}
128 	d = a.i[1];
129 	for (; i < ndigits && i < 14; i++) {
130 		buf[i] = hexchar[d >> 28];
131 		d <<= 4;
132 	}
133 	for (; i < ndigits; i++)
134 		buf[i] = '0';
135 	buf[ndigits] = '\0';
136 }
137 
138 void
139 __qaconvert(long double *arg, int ndigits, int *exp, int *sign, char *buf)
140 {
141 	union {
142 		unsigned int	i[4];
143 		long double	q;
144 	} a;
145 	enum fp_direction_type	rd;
146 	int			ha, i, s;
147 	unsigned int		b, r, d;
148 
149 	a.q = *arg;
150 	*sign = a.i[0] >> 31;
151 	ha = a.i[0] & ~0x80000000;
152 
153 	/* check for infinity or nan */
154 	if (ha >= 0x7fff0000) {
155 		*exp = 0;
156 		__infnanstring((ha == 0x7fff0000 && (a.i[1] | a.i[2] | a.i[3])
157 		    == 0)? fp_infinity : fp_quiet, ndigits, buf);
158 		return;
159 	}
160 
161 	/* check for subnormal or zero */
162 	if (ha < 0x00010000) {
163 		if ((ha | a.i[1] | a.i[2] | a.i[3]) == 0) {
164 			*exp = 0;
165 			for (i = 0; i < ndigits; i++)
166 				buf[i] = '0';
167 			buf[ndigits] = '\0';
168 			return;
169 		}
170 
171 		/* normalize */
172 		i = 0;
173 		while ((a.i[0] | (a.i[1] & 0xffff0000)) == 0) {
174 			a.i[0] = a.i[1];
175 			a.i[1] = a.i[2];
176 			a.i[2] = a.i[3];
177 			a.i[3] = 0;
178 			i += 32;
179 		}
180 		while ((a.i[0] & 0x7fff0000) == 0) {
181 			a.i[0] = (a.i[0] << 1) | (a.i[1] >> 31);
182 			a.i[1] = (a.i[1] << 1) | (a.i[2] >> 31);
183 			a.i[2] = (a.i[2] << 1) | (a.i[3] >> 31);
184 			a.i[3] <<= 1;
185 			i++;
186 		}
187 		*exp = -0x3ffe - i;
188 	} else {
189 		*exp = (ha >> 16) - 0x3fff;
190 	}
191 
192 	if (ndigits < 29) {
193 		/*
194 		 * Round the significand at the appropriate bit using
195 		 * integer arithmetic.  Explicitly raise the inexact
196 		 * exception if anything is rounded off.
197 		 */
198 		a.i[0] &= 0xffff;
199 		if (ndigits <= 5) {
200 			/*
201 			 * i and b are the index and bit position in a.i[]
202 			 * of the last bit to be retained.  r holds the bits
203 			 * to be rounded off, left-adjusted and sticky.
204 			 */
205 			i = 0;
206 			s = (5 - ndigits) << 2;
207 			b = 1 << s;
208 			r = ((a.i[0] << 1) << (31 - s)) | (a.i[1] >> s);
209 			if ((a.i[1] & (b - 1)) | a.i[2] | a.i[3])
210 				r |= 1;
211 			a.i[0] &= ~(b - 1);
212 			a.i[1] = a.i[2] = a.i[3] = 0;
213 		} else if (ndigits <= 13) {
214 			i = 1;
215 			s = (13 - ndigits) << 2;
216 			b = 1 << s;
217 			r = ((a.i[1] << 1) << (31 - s)) | (a.i[2] >> s);
218 			if ((a.i[2] & (b - 1)) | a.i[3])
219 				r |= 1;
220 			a.i[1] &= ~(b - 1);
221 			a.i[2] = a.i[3] = 0;
222 		} else if (ndigits <= 21) {
223 			i = 2;
224 			s = (21 - ndigits) << 2;
225 			b = 1 << s;
226 			r = ((a.i[2] << 1) << (31 - s)) | (a.i[3] >> s);
227 			if (a.i[3] & (b - 1))
228 				r |= 1;
229 			a.i[2] &= ~(b - 1);
230 			a.i[3] = 0;
231 		} else {
232 			i = 3;
233 			s = (29 - ndigits) << 2;
234 			b = 1 << s;
235 			r = (a.i[3] << 1) << (31 - s);
236 			a.i[3] &= ~(b - 1);
237 		}
238 
239 		/* conversion is inexact if r is not zero */
240 		if (r) {
241 			__base_conversion_set_exception(
242 			    (fp_exception_field_type)(1 << fp_inexact));
243 
244 			/* massage the rounding direction based on the sign */
245 			rd = _QgetRD();
246 			if (*sign && (rd == fp_positive || rd == fp_negative))
247 				rd = fp_positive + fp_negative - rd;
248 
249 			/* decide whether to round up */
250 			if (rd == fp_positive || (rd == fp_nearest &&
251 			    (r > 0x80000000u || (r == 0x80000000u &&
252 			    (a.i[i] & b))))) {
253 				a.i[i] += b;
254 				while (a.i[i] == 0)
255 					a.i[--i]++;
256 				if (a.i[0] >= 0x10000)
257 					(*exp)++;
258 			}
259 		}
260 	}
261 
262 	/* convert to hex digits */
263 	buf[0] = '1';
264 	d = a.i[0] << 16;
265 	for (i = 1; i < ndigits && i < 5; i++) {
266 		buf[i] = hexchar[d >> 28];
267 		d <<= 4;
268 	}
269 	d = a.i[1];
270 	for (; i < ndigits && i < 13; i++) {
271 		buf[i] = hexchar[d >> 28];
272 		d <<= 4;
273 	}
274 	d = a.i[2];
275 	for (; i < ndigits && i < 21; i++) {
276 		buf[i] = hexchar[d >> 28];
277 		d <<= 4;
278 	}
279 	d = a.i[3];
280 	for (; i < ndigits && i < 29; i++) {
281 		buf[i] = hexchar[d >> 28];
282 		d <<= 4;
283 	}
284 	for (; i < ndigits; i++)
285 		buf[i] = '0';
286 	buf[ndigits] = '\0';
287 }
288 
289 #elif defined(__i386) || defined(__amd64)
290 
291 /*
292  * The following code assumes the rounding precision mode is set
293  * to the default (round to 64 bits).
294  */
295 void
296 __qaconvert(long double *arg, int ndigits, int *exp, int *sign, char *buf)
297 {
298 	union {
299 		unsigned int	i[3];
300 		long double	x;
301 	} a, c;
302 	int		ea, i, s;
303 	unsigned int	d;
304 
305 	a.x = *arg;
306 	*sign = s = (a.i[2] >> 15) & 1;
307 	ea = a.i[2] & 0x7fff;
308 
309 	/* check for infinity or nan */
310 	if (ea == 0x7fff) {
311 		*exp = 0;
312 		__infnanstring((((a.i[1] << 1) | a.i[0]) == 0)?
313 		    fp_infinity : fp_quiet, ndigits, buf);
314 		return;
315 	}
316 
317 	/* check for subnormal or zero */
318 	if (ea == 0) {
319 		if ((a.i[1] | a.i[0]) == 0) {
320 			*exp = 0;
321 			for (i = 0; i < ndigits; i++)
322 				buf[i] = '0';
323 			buf[ndigits] = '\0';
324 			return;
325 		}
326 
327 		/* normalize */
328 		a.x *= 18446744073709551616.0; /* 2^64 */
329 		ea = a.i[2] & 0x7fff;
330 		*exp = ea - 0x403f;
331 	} else {
332 		*exp = ea - 0x3fff;
333 	}
334 
335 	if (ndigits < 17) {
336 		/*
337 		 * Round the significand at the appropriate bit by adding
338 		 * and subtracting a power of two.  This will also raise
339 		 * the inexact exception if anything is rounded off.
340 		 */
341 		c.i[2] = (0x4042 | (s << 15)) - (ndigits << 2);
342 		c.i[1] = 0x80000000;
343 		c.i[0] = 0;
344 		a.i[2] = 0x3fff | (s << 15);
345 		a.x = (a.x + c.x) - c.x;
346 		ea = a.i[2] & 0x7fff;
347 		if (ea >= 0x4000)
348 			(*exp)++;
349 	}
350 
351 	/* convert to hex digits */
352 	buf[0] = '1';
353 	d = (a.i[1] << 1) | (a.i[0] >> 31);
354 	for (i = 1; i < ndigits && i < 9; i++) {
355 		buf[i] = hexchar[d >> 28];
356 		d <<= 4;
357 	}
358 	d = a.i[0] << 1;
359 	for (; i < ndigits && i < 17; i++) {
360 		buf[i] = hexchar[d >> 28];
361 		d <<= 4;
362 	}
363 	for (; i < ndigits; i++)
364 		buf[i] = '0';
365 	buf[ndigits] = '\0';
366 }
367 
368 void
369 __aconvert(double arg, int ndigits, int *exp, int *sign, char *buf)
370 {
371 	union {
372 		int	i[2];
373 		double	d;
374 	} a;
375 	long double	ldarg;
376 	int		ha;
377 
378 	/* avoid raising invalid operation exception for signaling nan */
379 	a.i[0] = *(int *)&arg;
380 	a.i[1] = *(1+(int *)&arg);
381 	ha = a.i[1] & ~0x80000000;
382 	if (ha > 0x7ff00000 || (ha == 0x7ff00000 && a.i[0] != 0))
383 		a.i[1] |= 0x80000; /* make nan quiet */
384 	ldarg = a.d;
385 	__qaconvert(&ldarg, ndigits, exp, sign, buf);
386 }
387 
388 #else
389 #error Unknown architecture
390 #endif
391