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 2003 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 "quad.h"
30
31#ifdef __sparcv9
32
33/*
34 * _Qp_sub(pz, ox, oy) sets *pz = *ox - *oy.
35 */
36void
37_Qp_sub(union longdouble *pz, const union longdouble *ox,
38	const union longdouble *oy)
39
40#else
41
42/*
43 * _Q_sub(ox, oy) returns *ox - *oy.
44 */
45union longdouble
46_Q_sub(const union longdouble *ox, const union longdouble *oy)
47
48#endif	/* __sparcv9 */
49
50{
51	union longdouble	z;
52	const union longdouble	*x, *y;
53	unsigned int		xm, ym, tm, fsr;
54	int			flip;
55
56	/* sort so |x| >= |y| */
57	xm = ox->l.msw & 0x7fffffff;
58	ym = oy->l.msw & 0x7fffffff;
59	if (ym > xm || ym == xm && (oy->l.frac2 > ox->l.frac2 ||
60	    oy->l.frac2 == ox->l.frac2 && (oy->l.frac3 > ox->l.frac3 ||
61	    oy->l.frac3 == ox->l.frac3 && oy->l.frac4 > ox->l.frac4))) {
62		y = ox;
63		x = oy;
64		tm = xm;
65		xm = ym;
66		ym = tm;
67		flip = 0x80000000;
68	} else {
69		x = ox;
70		y = oy;
71		flip = 0;
72	}
73
74	/* get the fsr */
75	__quad_getfsrp(&fsr);
76
77	/* handle nan and inf cases */
78	if (xm >= 0x7fff0000) {
79		/* x is nan or inf */
80		if (ym >= 0x7fff0000) {
81			/* y is nan or inf */
82			if ((ym & 0xffff) | y->l.frac2 | y->l.frac3 |
83			    y->l.frac4) {
84				/* y is nan; x must be nan too */
85				/* the following logic implements V9 app. B */
86				if (!(ym & 0x8000)) {
87					/* y is snan, signal invalid */
88					if (fsr & FSR_NVM) {
89						__quad_fsubq(ox, oy, &Z);
90					} else {
91						Z = (xm & 0x8000)? *y : *oy;
92						Z.l.msw |= 0x8000;
93						fsr = (fsr & ~FSR_CEXC) |
94						    FSR_NVA | FSR_NVC;
95						__quad_setfsrp(&fsr);
96					}
97					QUAD_RETURN(Z);
98				}
99				/* x and y are both qnan */
100				Z = *oy;
101				QUAD_RETURN(Z);
102			}
103			if (!((xm & 0xffff) | x->l.frac2 | x->l.frac3 |
104			    x->l.frac4)) {
105				/* x and y are both inf */
106				if (!((x->l.msw ^ y->l.msw) & 0x80000000)) {
107					/* inf - inf, signal invalid */
108					if (fsr & FSR_NVM) {
109						__quad_fsubq(ox, oy, &Z);
110					} else {
111						Z.l.msw = 0x7fffffff;
112						Z.l.frac2 = Z.l.frac3 =
113						    Z.l.frac4 = 0xffffffff;
114						fsr = (fsr & ~FSR_CEXC) |
115						    FSR_NVA | FSR_NVC;
116						__quad_setfsrp(&fsr);
117					}
118					QUAD_RETURN(Z);
119				}
120				/* inf + inf, return inf */
121				Z = *x;
122				Z.l.msw ^= flip;
123				QUAD_RETURN(Z);
124			}
125		}
126		if ((xm & 0xffff) | x->l.frac2 | x->l.frac3 | x->l.frac4) {
127			/* x is nan */
128			if (!(xm & 0x8000)) {
129				/* snan, signal invalid */
130				if (fsr & FSR_NVM) {
131					__quad_fsubq(ox, oy, &Z);
132				} else {
133					Z = *x;
134					Z.l.msw |= 0x8000;
135					fsr = (fsr & ~FSR_CEXC) | FSR_NVA |
136					    FSR_NVC;
137					__quad_setfsrp(&fsr);
138				}
139				QUAD_RETURN(Z);
140			}
141			Z = *x;
142			QUAD_RETURN(Z);
143		}
144		/* x is inf */
145		Z = *x;
146		Z.l.msw ^= flip;
147		QUAD_RETURN(Z);
148	}
149
150	/* now x and y are finite and |x| >= |y| */
151	fsr &= ~FSR_CEXC;
152	z.l.msw = (x->l.msw & 0x80000000) ^ flip;
153	if ((x->l.msw ^ y->l.msw) & 0x80000000)
154		__quad_mag_add(x, y, &z, &fsr);
155	else
156		__quad_mag_sub(x, y, &z, &fsr);
157	if ((fsr & FSR_CEXC) & (fsr >> 23)) {
158		__quad_setfsrp(&fsr);
159		__quad_fsubq(ox, oy, &Z);
160	} else {
161		Z = z;
162		fsr |= (fsr & 0x1f) << 5;
163		__quad_setfsrp(&fsr);
164	}
165	QUAD_RETURN(Z);
166}
167