xref: /netbsd-src/lib/libm/src/s_remquol.c (revision 8657eb318ed0f14896c2befc64f16cc463489a76)
1 /*-
2  * ====================================================
3  * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
4  *
5  * Developed at SunSoft, a Sun Microsystems, Inc. business.
6  * Permission to use, copy, modify, and distribute this
7  * software is freely granted, provided that this notice
8  * is preserved.
9  * ====================================================
10  */
11 
12 #include <sys/cdefs.h>
13 
14 #include "namespace.h"
15 
16 #include <float.h>
17 #include <machine/ieee.h>
18 #include <stdint.h>
19 
20 #include "math.h"
21 #include "math_private.h"
22 
23 #ifdef __weak_alias
24 __weak_alias(remquol, _remquol)
25 #endif
26 
27 #ifdef __HAVE_LONG_DOUBLE
28 
29 #define	BIAS (LDBL_MAX_EXP - 1)
30 
31 #if EXT_FRACLBITS > 32
32 typedef	uint64_t manl_t;
33 #else
34 typedef	uint32_t manl_t;
35 #endif
36 
37 #if EXT_FRACHBITS > 32
38 typedef	uint64_t manh_t;
39 #else
40 typedef	uint32_t manh_t;
41 #endif
42 
43 #ifdef LDBL_IMPLICIT_NBIT
44 #define	LDBL_NBIT	0
45 #endif
46 
47 /*
48  * These macros add and remove an explicit integer bit in front of the
49  * fractional significand, if the architecture doesn't have such a bit
50  * by default already.
51  */
52 #ifdef LDBL_IMPLICIT_NBIT
53 #define	SET_NBIT(hx)	((hx) | (1ULL << EXT_FRACHBITS))
54 #define	HFRAC_BITS	EXT_FRACHBITS
55 #else
56 #define	SET_NBIT(hx)	(hx)
57 #define	HFRAC_BITS	(EXT_FRACHBITS - 1)
58 #endif
59 
60 #define	MANL_SHIFT	(EXT_FRACLBITS - 1)
61 
62 static const long double Zero[] = {0.0L, -0.0L};
63 
64 /*
65  * Return the IEEE remainder and set *quo to the last n bits of the
66  * quotient, rounded to the nearest integer.  We choose n=31 because
67  * we wind up computing all the integer bits of the quotient anyway as
68  * a side-effect of computing the remainder by the shift and subtract
69  * method.  In practice, this is far more bits than are needed to use
70  * remquo in reduction algorithms.
71  *
72  * Assumptions:
73  * - The low part of the significand fits in a manl_t exactly.
74  * - The high part of the significand fits in an int64_t with enough
75  *   room for an explicit integer bit in front of the fractional bits.
76  */
77 long double
78 remquol(long double x, long double y, int *quo)
79 {
80 	union ieee_ext_u ux, uy;
81 	int64_t hx,hz;	/* We need a carry bit even if EXT_FRACHBITS is 32. */
82 	manh_t hy;
83 	manl_t lx,ly,lz;
84 	int ix,iy,n,q,sx,sxy;
85 
86 	ux.extu_ld = x;
87 	uy.extu_ld = y;
88 	sx = ux.extu_sign;
89 	sxy = sx ^ uy.extu_sign;
90 	ux.extu_sign = 0;	/* |x| */
91 	uy.extu_sign = 0;	/* |y| */
92 
93     /* purge off exception values */
94 	if((uy.extu_exp|uy.extu_frach|uy.extu_fracl)==0 || /* y=0 */
95 	   (ux.extu_exp == BIAS + LDBL_MAX_EXP) ||	 /* or x not finite */
96 	   (uy.extu_exp == BIAS + LDBL_MAX_EXP &&
97 	    ((uy.extu_frach&~LDBL_NBIT)|uy.extu_fracl)!=0)) /* or y is NaN */
98 	    return nan_mix_op(x, y, *)/nan_mix_op(x, y, *);
99 	if(ux.extu_exp<=uy.extu_exp) {
100 	    if((ux.extu_exp<uy.extu_exp) ||
101 	       (ux.extu_frach<=uy.extu_frach &&
102 		(ux.extu_frach<uy.extu_frach ||
103 		 ux.extu_fracl<uy.extu_fracl))) {
104 		q = 0;
105 		goto fixup;	/* |x|<|y| return x or x-y */
106 	    }
107 	    if(ux.extu_frach==uy.extu_frach && ux.extu_fracl==uy.extu_fracl) {
108 		*quo = (sxy ? -1 : 1);
109 		return Zero[sx];	/* |x|=|y| return x*0*/
110 	    }
111 	}
112 
113     /* determine ix = ilogb(x) */
114 	if(ux.extu_exp == 0) {	/* subnormal x */
115 	    ux.extu_ld *= 0x1.0p512;
116 	    ix = ux.extu_exp - (BIAS + 512);
117 	} else {
118 	    ix = ux.extu_exp - BIAS;
119 	}
120 
121     /* determine iy = ilogb(y) */
122 	if(uy.extu_exp == 0) {	/* subnormal y */
123 	    uy.extu_ld *= 0x1.0p512;
124 	    iy = uy.extu_exp - (BIAS + 512);
125 	} else {
126 	    iy = uy.extu_exp - BIAS;
127 	}
128 
129     /* set up {hx,lx}, {hy,ly} and align y to x */
130 	hx = SET_NBIT(ux.extu_frach);
131 	hy = SET_NBIT(uy.extu_frach);
132 	lx = ux.extu_fracl;
133 	ly = uy.extu_fracl;
134 
135     /* fix point fmod */
136 	n = ix - iy;
137 	q = 0;
138 	while(n--) {
139 	    hz=hx-hy;lz=lx-ly; if(lx<ly) hz -= 1;
140 	    if(hz<0){hx = hx+hx+(lx>>MANL_SHIFT); lx = lx+lx;}
141 	    else {hx = hz+hz+(lz>>MANL_SHIFT); lx = lz+lz; q++;}
142 	    q <<= 1;
143 	}
144 	hz=hx-hy;lz=lx-ly; if(lx<ly) hz -= 1;
145 	if(hz>=0) {hx=hz;lx=lz;q++;}
146 
147     /* convert back to floating value and restore the sign */
148 	if((hx|lx)==0) {			/* return sign(x)*0 */
149 	    q &= 0x7fffffff;
150 	    *quo = (sxy ? -q : q);
151 	    return Zero[sx];
152 	}
153 	while(hx<(int64_t)(1ULL<<HFRAC_BITS)) {	/* normalize x */
154 	    hx = hx+hx+(lx>>MANL_SHIFT); lx = lx+lx;
155 	    iy -= 1;
156 	}
157 	ux.extu_frach = hx; /* The integer bit is truncated here if needed. */
158 	ux.extu_fracl = lx;
159 	if (iy < LDBL_MIN_EXP) {
160 	    ux.extu_exp = iy + (BIAS + 512);
161 	    ux.extu_ld *= 0x1p-512;
162 	} else {
163 	    ux.extu_exp = iy + BIAS;
164 	}
165 fixup:
166 	x = ux.extu_ld;		/* |x| */
167 	y = fabsl(y);
168 	if (y < LDBL_MIN * 2) {
169 	    if (x+x>y || (x+x==y && (q & 1))) {
170 		q++;
171 		x-=y;
172 	    }
173 	} else if (x>0.5*y || (x==0.5*y && (q & 1))) {
174 	    q++;
175 	    x-=y;
176 	}
177 	ux.extu_ld = x;
178 	ux.extu_sign ^= sx;
179 	x = ux.extu_ld;
180 	q &= 0x7fffffff;
181 	*quo = (sxy ? -q : q);
182 	return x;
183 }
184 #else
185 
186 long double
187 remquol(long double x, long double y, int *quo)
188 {
189 	return remquo(x, y, quo);
190 }
191 
192 #endif
193