xref: /onnv-gate/usr/src/lib/libc/sparc/fp/__quad_mag.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright (c) 1994-1997, by Sun Microsystems, Inc.
24*0Sstevel@tonic-gate  * All rights reserved.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * This file contains __quad_mag_add and __quad_mag_sub, the core
31*0Sstevel@tonic-gate  * of the quad precision add and subtract operations.
32*0Sstevel@tonic-gate  */
33*0Sstevel@tonic-gate 
34*0Sstevel@tonic-gate #include "quad.h"
35*0Sstevel@tonic-gate 
36*0Sstevel@tonic-gate /*
37*0Sstevel@tonic-gate  * __quad_mag_add(x, y, z, fsr)
38*0Sstevel@tonic-gate  *
39*0Sstevel@tonic-gate  * Sets *z = *x + *y, rounded according to the rounding mode in *fsr,
40*0Sstevel@tonic-gate  * and updates the current exceptions in *fsr.  This routine assumes
41*0Sstevel@tonic-gate  * *x and *y are finite, with the same sign (i.e., an addition of
42*0Sstevel@tonic-gate  * magnitudes), |*x| >= |*y|, and *z already has its sign bit set.
43*0Sstevel@tonic-gate  */
44*0Sstevel@tonic-gate void
__quad_mag_add(const union longdouble * x,const union longdouble * y,union longdouble * z,unsigned int * fsr)45*0Sstevel@tonic-gate __quad_mag_add(const union longdouble *x, const union longdouble *y,
46*0Sstevel@tonic-gate 	union longdouble *z, unsigned int *fsr)
47*0Sstevel@tonic-gate {
48*0Sstevel@tonic-gate 	unsigned int	lx, ly, ex, ey, frac2, frac3, frac4;
49*0Sstevel@tonic-gate 	unsigned int	round, sticky, carry, rm;
50*0Sstevel@tonic-gate 	int		e, uflo;
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate 	/* get the leading significand words and exponents */
53*0Sstevel@tonic-gate 	ex = (x->l.msw & 0x7fffffff) >> 16;
54*0Sstevel@tonic-gate 	lx = x->l.msw & 0xffff;
55*0Sstevel@tonic-gate 	if (ex == 0)
56*0Sstevel@tonic-gate 		ex = 1;
57*0Sstevel@tonic-gate 	else
58*0Sstevel@tonic-gate 		lx |= 0x10000;
59*0Sstevel@tonic-gate 
60*0Sstevel@tonic-gate 	ey = (y->l.msw & 0x7fffffff) >> 16;
61*0Sstevel@tonic-gate 	ly = y->l.msw & 0xffff;
62*0Sstevel@tonic-gate 	if (ey == 0)
63*0Sstevel@tonic-gate 		ey = 1;
64*0Sstevel@tonic-gate 	else
65*0Sstevel@tonic-gate 		ly |= 0x10000;
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate 	/* prenormalize y */
68*0Sstevel@tonic-gate 	e = (int) ex - (int) ey;
69*0Sstevel@tonic-gate 	round = sticky = 0;
70*0Sstevel@tonic-gate 	if (e >= 114) {
71*0Sstevel@tonic-gate 		frac2 = x->l.frac2;
72*0Sstevel@tonic-gate 		frac3 = x->l.frac3;
73*0Sstevel@tonic-gate 		frac4 = x->l.frac4;
74*0Sstevel@tonic-gate 		sticky = ly | y->l.frac2 | y->l.frac3 | y->l.frac4;
75*0Sstevel@tonic-gate 	} else {
76*0Sstevel@tonic-gate 		frac2 = y->l.frac2;
77*0Sstevel@tonic-gate 		frac3 = y->l.frac3;
78*0Sstevel@tonic-gate 		frac4 = y->l.frac4;
79*0Sstevel@tonic-gate 		if (e >= 96) {
80*0Sstevel@tonic-gate 			sticky = frac4 | frac3 | (frac2 & 0x7fffffff);
81*0Sstevel@tonic-gate 			round = frac2 & 0x80000000;
82*0Sstevel@tonic-gate 			frac4 = ly;
83*0Sstevel@tonic-gate 			frac3 = frac2 = ly = 0;
84*0Sstevel@tonic-gate 			e -= 96;
85*0Sstevel@tonic-gate 		} else if (e >= 64) {
86*0Sstevel@tonic-gate 			sticky = frac4 | (frac3 & 0x7fffffff);
87*0Sstevel@tonic-gate 			round = frac3 & 0x80000000;
88*0Sstevel@tonic-gate 			frac4 = frac2;
89*0Sstevel@tonic-gate 			frac3 = ly;
90*0Sstevel@tonic-gate 			frac2 = ly = 0;
91*0Sstevel@tonic-gate 			e -= 64;
92*0Sstevel@tonic-gate 		} else if (e >= 32) {
93*0Sstevel@tonic-gate 			sticky = frac4 & 0x7fffffff;
94*0Sstevel@tonic-gate 			round = frac4 & 0x80000000;
95*0Sstevel@tonic-gate 			frac4 = frac3;
96*0Sstevel@tonic-gate 			frac3 = frac2;
97*0Sstevel@tonic-gate 			frac2 = ly;
98*0Sstevel@tonic-gate 			ly = 0;
99*0Sstevel@tonic-gate 			e -= 32;
100*0Sstevel@tonic-gate 		}
101*0Sstevel@tonic-gate 		if (e) {
102*0Sstevel@tonic-gate 			sticky |= round | (frac4 & ((1 << (e - 1)) - 1));
103*0Sstevel@tonic-gate 			round = frac4 & (1 << (e - 1));
104*0Sstevel@tonic-gate 			frac4 = (frac4 >> e) | (frac3 << (32 - e));
105*0Sstevel@tonic-gate 			frac3 = (frac3 >> e) | (frac2 << (32 - e));
106*0Sstevel@tonic-gate 			frac2 = (frac2 >> e) | (ly << (32 - e));
107*0Sstevel@tonic-gate 			ly >>= e;
108*0Sstevel@tonic-gate 		}
109*0Sstevel@tonic-gate 
110*0Sstevel@tonic-gate 		/* add, propagating carries */
111*0Sstevel@tonic-gate 		frac4 += x->l.frac4;
112*0Sstevel@tonic-gate 		carry = (frac4 < x->l.frac4);
113*0Sstevel@tonic-gate 		frac3 += x->l.frac3;
114*0Sstevel@tonic-gate 		if (carry) {
115*0Sstevel@tonic-gate 			frac3++;
116*0Sstevel@tonic-gate 			carry = (frac3 <= x->l.frac3);
117*0Sstevel@tonic-gate 		} else {
118*0Sstevel@tonic-gate 			carry = (frac3 < x->l.frac3);
119*0Sstevel@tonic-gate 		}
120*0Sstevel@tonic-gate 		frac2 += x->l.frac2;
121*0Sstevel@tonic-gate 		if (carry) {
122*0Sstevel@tonic-gate 			frac2++;
123*0Sstevel@tonic-gate 			carry = (frac2 <= x->l.frac2);
124*0Sstevel@tonic-gate 		} else {
125*0Sstevel@tonic-gate 			carry = (frac2 < x->l.frac2);
126*0Sstevel@tonic-gate 		}
127*0Sstevel@tonic-gate 		lx += ly;
128*0Sstevel@tonic-gate 		if (carry)
129*0Sstevel@tonic-gate 			lx++;
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate 		/* postnormalize */
132*0Sstevel@tonic-gate 		if (lx >= 0x20000) {
133*0Sstevel@tonic-gate 			sticky |= round;
134*0Sstevel@tonic-gate 			round = frac4 & 1;
135*0Sstevel@tonic-gate 			frac4 = (frac4 >> 1) | (frac3 << 31);
136*0Sstevel@tonic-gate 			frac3 = (frac3 >> 1) | (frac2 << 31);
137*0Sstevel@tonic-gate 			frac2 = (frac2 >> 1) | (lx << 31);
138*0Sstevel@tonic-gate 			lx >>= 1;
139*0Sstevel@tonic-gate 			ex++;
140*0Sstevel@tonic-gate 		}
141*0Sstevel@tonic-gate 	}
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate 	/* keep track of whether the result before rounding is tiny */
144*0Sstevel@tonic-gate 	uflo = (lx < 0x10000);
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate 	/* get the rounding mode, fudging directed rounding modes */
147*0Sstevel@tonic-gate 	/* as though the result were positive */
148*0Sstevel@tonic-gate 	rm = *fsr >> 30;
149*0Sstevel@tonic-gate 	if (z->l.msw)
150*0Sstevel@tonic-gate 		rm ^= (rm >> 1);
151*0Sstevel@tonic-gate 
152*0Sstevel@tonic-gate 	/* see if we need to round */
153*0Sstevel@tonic-gate 	if (round | sticky) {
154*0Sstevel@tonic-gate 		*fsr |= FSR_NXC;
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate 		/* round up if necessary */
157*0Sstevel@tonic-gate 		if (rm == FSR_RP || (rm == FSR_RN && round &&
158*0Sstevel@tonic-gate 			(sticky || (frac4 & 1)))) {
159*0Sstevel@tonic-gate 			if (++frac4 == 0)
160*0Sstevel@tonic-gate 				if (++frac3 == 0)
161*0Sstevel@tonic-gate 					if (++frac2 == 0)
162*0Sstevel@tonic-gate 						if (++lx >= 0x20000) {
163*0Sstevel@tonic-gate 							lx >>= 1;
164*0Sstevel@tonic-gate 							ex++;
165*0Sstevel@tonic-gate 						}
166*0Sstevel@tonic-gate 		}
167*0Sstevel@tonic-gate 	}
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate 	/* check for overflow */
170*0Sstevel@tonic-gate 	if (ex >= 0x7fff) {
171*0Sstevel@tonic-gate 		/* store the default overflowed result */
172*0Sstevel@tonic-gate 		*fsr |= FSR_OFC | FSR_NXC;
173*0Sstevel@tonic-gate 		if (rm == FSR_RN || rm == FSR_RP) {
174*0Sstevel@tonic-gate 			z->l.msw |= 0x7fff0000;
175*0Sstevel@tonic-gate 			z->l.frac2 = z->l.frac3 = z->l.frac4 = 0;
176*0Sstevel@tonic-gate 		} else {
177*0Sstevel@tonic-gate 			z->l.msw |= 0x7ffeffff;
178*0Sstevel@tonic-gate 			z->l.frac2 = z->l.frac3 = z->l.frac4 = 0xffffffff;
179*0Sstevel@tonic-gate 		}
180*0Sstevel@tonic-gate 	} else {
181*0Sstevel@tonic-gate 		/* store the result */
182*0Sstevel@tonic-gate 		if (lx >= 0x10000)
183*0Sstevel@tonic-gate 			z->l.msw |= (ex << 16);
184*0Sstevel@tonic-gate 		z->l.msw |= (lx & 0xffff);
185*0Sstevel@tonic-gate 		z->l.frac2 = frac2;
186*0Sstevel@tonic-gate 		z->l.frac3 = frac3;
187*0Sstevel@tonic-gate 		z->l.frac4 = frac4;
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate 		/* if the pre-rounded result was tiny and underflow trapping */
190*0Sstevel@tonic-gate 		/* is enabled, simulate underflow */
191*0Sstevel@tonic-gate 		if (uflo && (*fsr & FSR_UFM))
192*0Sstevel@tonic-gate 			*fsr |= FSR_UFC;
193*0Sstevel@tonic-gate 	}
194*0Sstevel@tonic-gate }
195*0Sstevel@tonic-gate 
196*0Sstevel@tonic-gate /*
197*0Sstevel@tonic-gate  * __quad_mag_sub(x, y, z, fsr)
198*0Sstevel@tonic-gate  *
199*0Sstevel@tonic-gate  * Sets *z = *x - *y, rounded according to the rounding mode in *fsr,
200*0Sstevel@tonic-gate  * and updates the current exceptions in *fsr.  This routine assumes
201*0Sstevel@tonic-gate  * *x and *y are finite, with opposite signs (i.e., a subtraction of
202*0Sstevel@tonic-gate  * magnitudes), |*x| >= |*y|, and *z already has its sign bit set.
203*0Sstevel@tonic-gate  */
204*0Sstevel@tonic-gate void
__quad_mag_sub(const union longdouble * x,const union longdouble * y,union longdouble * z,unsigned int * fsr)205*0Sstevel@tonic-gate __quad_mag_sub(const union longdouble *x, const union longdouble *y,
206*0Sstevel@tonic-gate 	union longdouble *z, unsigned int *fsr)
207*0Sstevel@tonic-gate {
208*0Sstevel@tonic-gate 	unsigned int	lx, ly, ex, ey, frac2, frac3, frac4;
209*0Sstevel@tonic-gate 	unsigned int	guard, round, sticky, borrow, rm;
210*0Sstevel@tonic-gate 	int		e;
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 	/* get the leading significand words and exponents */
213*0Sstevel@tonic-gate 	ex = (x->l.msw & 0x7fffffff) >> 16;
214*0Sstevel@tonic-gate 	lx = x->l.msw & 0xffff;
215*0Sstevel@tonic-gate 	if (ex == 0)
216*0Sstevel@tonic-gate 		ex = 1;
217*0Sstevel@tonic-gate 	else
218*0Sstevel@tonic-gate 		lx |= 0x10000;
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate 	ey = (y->l.msw & 0x7fffffff) >> 16;
221*0Sstevel@tonic-gate 	ly = y->l.msw & 0xffff;
222*0Sstevel@tonic-gate 	if (ey == 0)
223*0Sstevel@tonic-gate 		ey = 1;
224*0Sstevel@tonic-gate 	else
225*0Sstevel@tonic-gate 		ly |= 0x10000;
226*0Sstevel@tonic-gate 
227*0Sstevel@tonic-gate 	/* prenormalize y */
228*0Sstevel@tonic-gate 	e = (int) ex - (int) ey;
229*0Sstevel@tonic-gate 	guard = round = sticky = 0;
230*0Sstevel@tonic-gate 	if (e > 114) {
231*0Sstevel@tonic-gate 		sticky = ly | y->l.frac2 | y->l.frac3 | y->l.frac4;
232*0Sstevel@tonic-gate 		ly = frac2 = frac3 = frac4 = 0;
233*0Sstevel@tonic-gate 	} else {
234*0Sstevel@tonic-gate 		frac2 = y->l.frac2;
235*0Sstevel@tonic-gate 		frac3 = y->l.frac3;
236*0Sstevel@tonic-gate 		frac4 = y->l.frac4;
237*0Sstevel@tonic-gate 		if (e >= 96) {
238*0Sstevel@tonic-gate 			sticky = frac4 | frac3 | (frac2 & 0x3fffffff);
239*0Sstevel@tonic-gate 			round = frac2 & 0x40000000;
240*0Sstevel@tonic-gate 			guard = frac2 & 0x80000000;
241*0Sstevel@tonic-gate 			frac4 = ly;
242*0Sstevel@tonic-gate 			frac3 = frac2 = ly = 0;
243*0Sstevel@tonic-gate 			e -= 96;
244*0Sstevel@tonic-gate 		} else if (e >= 64) {
245*0Sstevel@tonic-gate 			sticky = frac4 | (frac3 & 0x3fffffff);
246*0Sstevel@tonic-gate 			round = frac3 & 0x40000000;
247*0Sstevel@tonic-gate 			guard = frac3 & 0x80000000;
248*0Sstevel@tonic-gate 			frac4 = frac2;
249*0Sstevel@tonic-gate 			frac3 = ly;
250*0Sstevel@tonic-gate 			frac2 = ly = 0;
251*0Sstevel@tonic-gate 			e -= 64;
252*0Sstevel@tonic-gate 		} else if (e >= 32) {
253*0Sstevel@tonic-gate 			sticky = frac4 & 0x3fffffff;
254*0Sstevel@tonic-gate 			round = frac4 & 0x40000000;
255*0Sstevel@tonic-gate 			guard = frac4 & 0x80000000;
256*0Sstevel@tonic-gate 			frac4 = frac3;
257*0Sstevel@tonic-gate 			frac3 = frac2;
258*0Sstevel@tonic-gate 			frac2 = ly;
259*0Sstevel@tonic-gate 			ly = 0;
260*0Sstevel@tonic-gate 			e -= 32;
261*0Sstevel@tonic-gate 		}
262*0Sstevel@tonic-gate 		if (e > 1) {
263*0Sstevel@tonic-gate 			sticky |= guard | round |
264*0Sstevel@tonic-gate 				(frac4 & ((1 << (e - 2)) - 1));
265*0Sstevel@tonic-gate 			round = frac4 & (1 << (e - 2));
266*0Sstevel@tonic-gate 			guard = frac4 & (1 << (e - 1));
267*0Sstevel@tonic-gate 			frac4 = (frac4 >> e) | (frac3 << (32 - e));
268*0Sstevel@tonic-gate 			frac3 = (frac3 >> e) | (frac2 << (32 - e));
269*0Sstevel@tonic-gate 			frac2 = (frac2 >> e) | (ly << (32 - e));
270*0Sstevel@tonic-gate 			ly >>= e;
271*0Sstevel@tonic-gate 		} else if (e == 1) {
272*0Sstevel@tonic-gate 			sticky |= round;
273*0Sstevel@tonic-gate 			round = guard;
274*0Sstevel@tonic-gate 			guard = frac4 & 1;
275*0Sstevel@tonic-gate 			frac4 = (frac4 >> 1) | (frac3 << 31);
276*0Sstevel@tonic-gate 			frac3 = (frac3 >> 1) | (frac2 << 31);
277*0Sstevel@tonic-gate 			frac2 = (frac2 >> 1) | (ly << 31);
278*0Sstevel@tonic-gate 			ly >>= 1;
279*0Sstevel@tonic-gate 		}
280*0Sstevel@tonic-gate 	}
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate 	/* complement guard, round, and sticky as need be */
283*0Sstevel@tonic-gate 	if (sticky) {
284*0Sstevel@tonic-gate 		round = !round;
285*0Sstevel@tonic-gate 		guard = !guard;
286*0Sstevel@tonic-gate 	} else if (round) {
287*0Sstevel@tonic-gate 		guard = !guard;
288*0Sstevel@tonic-gate 	}
289*0Sstevel@tonic-gate 	borrow = (guard | round | sticky);
290*0Sstevel@tonic-gate 
291*0Sstevel@tonic-gate 	/* subtract, propagating borrows */
292*0Sstevel@tonic-gate 	frac4 = x->l.frac4 - frac4;
293*0Sstevel@tonic-gate 	if (borrow) {
294*0Sstevel@tonic-gate 		frac4--;
295*0Sstevel@tonic-gate 		borrow = (frac4 >= x->l.frac4);
296*0Sstevel@tonic-gate 	} else {
297*0Sstevel@tonic-gate 		borrow = (frac4 > x->l.frac4);
298*0Sstevel@tonic-gate 	}
299*0Sstevel@tonic-gate 	frac3 = x->l.frac3 - frac3;
300*0Sstevel@tonic-gate 	if (borrow) {
301*0Sstevel@tonic-gate 		frac3--;
302*0Sstevel@tonic-gate 		borrow = (frac3 >= x->l.frac3);
303*0Sstevel@tonic-gate 	} else {
304*0Sstevel@tonic-gate 		borrow = (frac3 > x->l.frac3);
305*0Sstevel@tonic-gate 	}
306*0Sstevel@tonic-gate 	frac2 = x->l.frac2 - frac2;
307*0Sstevel@tonic-gate 	if (borrow) {
308*0Sstevel@tonic-gate 		frac2--;
309*0Sstevel@tonic-gate 		borrow = (frac2 >= x->l.frac2);
310*0Sstevel@tonic-gate 	} else {
311*0Sstevel@tonic-gate 		borrow = (frac2 > x->l.frac2);
312*0Sstevel@tonic-gate 	}
313*0Sstevel@tonic-gate 	lx -= ly;
314*0Sstevel@tonic-gate 	if (borrow)
315*0Sstevel@tonic-gate 		lx--;
316*0Sstevel@tonic-gate 
317*0Sstevel@tonic-gate 	/* get the rounding mode */
318*0Sstevel@tonic-gate 	rm = *fsr >> 30;
319*0Sstevel@tonic-gate 
320*0Sstevel@tonic-gate 	/* handle zero result */
321*0Sstevel@tonic-gate 	if (!(lx | frac2 | frac3 | frac4 | guard)) {
322*0Sstevel@tonic-gate 		z->l.msw = ((rm == FSR_RM)? 0x80000000 : 0);
323*0Sstevel@tonic-gate 		z->l.frac2 = z->l.frac3 = z->l.frac4 = 0;
324*0Sstevel@tonic-gate 		return;
325*0Sstevel@tonic-gate 	}
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate 	/* postnormalize */
328*0Sstevel@tonic-gate 	if (lx < 0x10000) {
329*0Sstevel@tonic-gate 		/* if cancellation occurred or the exponent is 1, */
330*0Sstevel@tonic-gate 		/* the result is exact */
331*0Sstevel@tonic-gate 		if (lx < 0x8000 || ex == 1) {
332*0Sstevel@tonic-gate 			while ((lx | (frac2 & 0xfffe0000)) == 0 && ex > 32) {
333*0Sstevel@tonic-gate 				lx = frac2;
334*0Sstevel@tonic-gate 				frac2 = frac3;
335*0Sstevel@tonic-gate 				frac3 = frac4;
336*0Sstevel@tonic-gate 				frac4 = ((guard)? 0x80000000 : 0);
337*0Sstevel@tonic-gate 				guard = 0;
338*0Sstevel@tonic-gate 				ex -= 32;
339*0Sstevel@tonic-gate 			}
340*0Sstevel@tonic-gate 			while (lx < 0x10000 && ex > 1) {
341*0Sstevel@tonic-gate 				lx = (lx << 1) | (frac2 >> 31);
342*0Sstevel@tonic-gate 				frac2 = (frac2 << 1) | (frac3 >> 31);
343*0Sstevel@tonic-gate 				frac3 = (frac3 << 1) | (frac4 >> 31);
344*0Sstevel@tonic-gate 				frac4 <<= 1;
345*0Sstevel@tonic-gate 				if (guard) {
346*0Sstevel@tonic-gate 					frac4 |= 1;
347*0Sstevel@tonic-gate 					guard = 0;
348*0Sstevel@tonic-gate 				}
349*0Sstevel@tonic-gate 				ex--;
350*0Sstevel@tonic-gate 			}
351*0Sstevel@tonic-gate 			if (lx >= 0x10000)
352*0Sstevel@tonic-gate 				z->l.msw |= (ex << 16);
353*0Sstevel@tonic-gate 			z->l.msw |= (lx & 0xffff);
354*0Sstevel@tonic-gate 			z->l.frac2 = frac2;
355*0Sstevel@tonic-gate 			z->l.frac3 = frac3;
356*0Sstevel@tonic-gate 			z->l.frac4 = frac4;
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate 			/* if the result is tiny and underflow trapping is */
359*0Sstevel@tonic-gate 			/* enabled, simulate underflow */
360*0Sstevel@tonic-gate 			if (lx < 0x10000 && (*fsr & FSR_UFM))
361*0Sstevel@tonic-gate 				*fsr |= FSR_UFC;
362*0Sstevel@tonic-gate 			return;
363*0Sstevel@tonic-gate 		}
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate 		/* otherwise we only borrowed one place */
366*0Sstevel@tonic-gate 		lx = (lx << 1) | (frac2 >> 31);
367*0Sstevel@tonic-gate 		frac2 = (frac2 << 1) | (frac3 >> 31);
368*0Sstevel@tonic-gate 		frac3 = (frac3 << 1) | (frac4 >> 31);
369*0Sstevel@tonic-gate 		frac4 <<= 1;
370*0Sstevel@tonic-gate 		if (guard)
371*0Sstevel@tonic-gate 			frac4 |= 1;
372*0Sstevel@tonic-gate 		ex--;
373*0Sstevel@tonic-gate 	} else {
374*0Sstevel@tonic-gate 		sticky |= round;
375*0Sstevel@tonic-gate 		round = guard;
376*0Sstevel@tonic-gate 	}
377*0Sstevel@tonic-gate 
378*0Sstevel@tonic-gate 	/* fudge directed rounding modes as though the result were positive */
379*0Sstevel@tonic-gate 	if (z->l.msw)
380*0Sstevel@tonic-gate 		rm ^= (rm >> 1);
381*0Sstevel@tonic-gate 
382*0Sstevel@tonic-gate 	/* see if we need to round */
383*0Sstevel@tonic-gate 	if (round | sticky) {
384*0Sstevel@tonic-gate 		*fsr |= FSR_NXC;
385*0Sstevel@tonic-gate 
386*0Sstevel@tonic-gate 		/* round up if necessary */
387*0Sstevel@tonic-gate 		if (rm == FSR_RP || (rm == FSR_RN && round &&
388*0Sstevel@tonic-gate 			(sticky || (frac4 & 1)))) {
389*0Sstevel@tonic-gate 			if (++frac4 == 0)
390*0Sstevel@tonic-gate 				if (++frac3 == 0)
391*0Sstevel@tonic-gate 					if (++frac2 == 0)
392*0Sstevel@tonic-gate 						if (++lx >= 0x20000) {
393*0Sstevel@tonic-gate 							lx >>= 1;
394*0Sstevel@tonic-gate 							ex++;
395*0Sstevel@tonic-gate 						}
396*0Sstevel@tonic-gate 		}
397*0Sstevel@tonic-gate 	}
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate 	/* store the result */
400*0Sstevel@tonic-gate 	z->l.msw |= (ex << 16) | (lx & 0xffff);
401*0Sstevel@tonic-gate 	z->l.frac2 = frac2;
402*0Sstevel@tonic-gate 	z->l.frac3 = frac3;
403*0Sstevel@tonic-gate 	z->l.frac4 = frac4;
404*0Sstevel@tonic-gate }
405