xref: /illumos-gate/usr/src/lib/libc/sparc/fp/_Q_qtod.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
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 (c) 1994-1997, by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "quad.h"
30 
31 #ifdef __sparcv9
32 #define	_Q_qtod	_Qp_qtod
33 #endif
34 
35 /*
36  * _Q_qtod(x) returns (double)*x.
37  */
38 double
39 _Q_qtod(const union longdouble *x)
40 {
41 	union xdouble	u;
42 	unsigned int	xm, round, sticky, fsr, rm;
43 	int		subnormal, e;
44 
45 	xm = x->l.msw & 0x7fffffff;
46 
47 	/* get the rounding mode, fudging directed rounding modes */
48 	/* as though the result were positive */
49 	__quad_getfsrp(&fsr);
50 	rm = fsr >> 30;
51 	if (x->l.msw & 0x80000000)
52 		rm ^= (rm >> 1);
53 
54 	/* handle nan, inf, and out-of-range cases */
55 	if (xm >= 0x43ff0000) {
56 		if (xm >= 0x7fff0000) {
57 			if ((xm & 0xffff) | x->l.frac2 | x->l.frac3 |
58 			    x->l.frac4) {
59 				/* x is nan */
60 				u.l.hi = (x->l.msw & 0x80000000) | 0x7ff80000;
61 				u.l.hi |= ((xm & 0x7fff) << 4) |
62 				    (x->l.frac2 >> 28);
63 				u.l.lo = (x->l.frac2 << 4) |
64 				    (x->l.frac3 >> 28);
65 				if (!(xm & 0x8000)) {
66 					/* snan, signal invalid */
67 					if (fsr & FSR_NVM) {
68 						__quad_fqtod(x, &u.d);
69 					} else {
70 						fsr = (fsr & ~FSR_CEXC) |
71 						    FSR_NVA | FSR_NVC;
72 						__quad_setfsrp(&fsr);
73 					}
74 				}
75 				return (u.d);
76 			}
77 			/* x is inf */
78 			u.l.hi = (x->l.msw & 0x80000000) | 0x7ff00000;
79 			u.l.lo = 0;
80 			return (u.d);
81 		}
82 		/* x is too big, overflow */
83 		if (rm == FSR_RN || rm == FSR_RP) {
84 			u.l.hi = 0x7ff00000;
85 			u.l.lo = 0;
86 		} else {
87 			u.l.hi = 0x7fefffff;
88 			u.l.lo = 0xffffffff;
89 		}
90 		u.l.hi |= (x->l.msw & 0x80000000);
91 		if (fsr & (FSR_OFM | FSR_NXM)) {
92 			__quad_fqtod(x, &u.d);
93 		} else {
94 			fsr = (fsr & ~FSR_CEXC) | FSR_OFA | FSR_OFC |
95 			    FSR_NXA | FSR_NXC;
96 			__quad_setfsrp(&fsr);
97 		}
98 		return (u.d);
99 	}
100 
101 	subnormal = 0;
102 	if (xm < 0x3c010000) {
103 		if (xm < 0x3bcc0000) {
104 			if (QUAD_ISZERO(*x)) {
105 				u.l.hi = (x->l.msw & 0x80000000);
106 				u.l.lo = 0;
107 				return (u.d);
108 			}
109 			/* x is too small, underflow */
110 			u.l.hi = (x->l.msw & 0x80000000);
111 			u.l.lo = ((rm == FSR_RP)? 1 : 0);
112 			if (fsr & (FSR_UFM | FSR_NXM)) {
113 				__quad_fqtod(x, &u.d);
114 			} else {
115 				fsr = (fsr & ~FSR_CEXC) | FSR_UFA | FSR_UFC |
116 				    FSR_NXA | FSR_NXC;
117 				__quad_setfsrp(&fsr);
118 			}
119 			return (u.d);
120 		}
121 
122 		/* x is in the subnormal range for double */
123 		subnormal = 1;
124 		u.l.hi = 0x80000 | ((xm & 0xffff) << 3) | (x->l.frac2 >> 29);
125 		u.l.lo = (x->l.frac2 << 3) | (x->l.frac3 >> 29);
126 		round = x->l.frac3 & 0x10000000;
127 		sticky = (x->l.frac3 & 0xfffffff) | x->l.frac4;
128 		e = 0x3c00 - (xm >> 16);
129 		if (e >= 32) {
130 			sticky |= round | (u.l.lo & 0x7fffffff);
131 			round = u.l.lo & 0x80000000;
132 			u.l.lo = u.l.hi;
133 			u.l.hi = 0;
134 			e -= 32;
135 		}
136 		if (e) {
137 			sticky |= round | (u.l.lo & ((1 << (e - 1)) - 1));
138 			round = u.l.lo & (1 << (e - 1));
139 			u.l.lo = (u.l.lo >> e) | (u.l.hi << (32 - e));
140 			u.l.hi >>= e;
141 		}
142 	} else {
143 		/* x is in the normal range for double */
144 		u.l.hi = ((xm - 0x3c000000) << 4) | (x->l.frac2 >> 28);
145 		u.l.lo = (x->l.frac2 << 4) | (x->l.frac3 >> 28);
146 		round = x->l.frac3 & 0x8000000;
147 		sticky = (x->l.frac3 & 0x7ffffff) | x->l.frac4;
148 	}
149 
150 	/* see if we need to round */
151 	fsr &= ~FSR_CEXC;
152 	if (round | sticky) {
153 		fsr |= FSR_NXC;
154 		if (subnormal)
155 			fsr |= FSR_UFC;
156 
157 		/* round up if necessary */
158 		if (rm == FSR_RP || (rm == FSR_RN && round && (sticky ||
159 		    (u.l.lo & 1)))) {
160 			/* round up and check for overflow */
161 			if (++u.l.lo == 0)
162 				if (++u.l.hi >= 0x7ff00000)
163 					fsr |= FSR_OFC;
164 		}
165 	}
166 
167 	/* if result is exact and subnormal but underflow trapping is */
168 	/* enabled, signal underflow */
169 	else if (subnormal && (fsr & FSR_UFM))
170 		fsr |= FSR_UFC;
171 
172 	/* attach the sign and raise exceptions as need be */
173 	u.l.hi |= (x->l.msw & 0x80000000);
174 	if ((fsr & FSR_CEXC) & (fsr >> 23)) {
175 		__quad_setfsrp(&fsr);
176 		__quad_fqtod(x, &u.d);
177 	} else {
178 		fsr |= (fsr & 0x1f) << 5;
179 		__quad_setfsrp(&fsr);
180 	}
181 	return (u.d);
182 }
183