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