Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / i386 / gnu / fpemul / poly_l2.c
1 /*
2  *  poly_l2.c
3  *
4  * Compute the base 2 log of a FPU_REG, using a polynomial approximation.
5  *
6  *
7  * Copyright (C) 1992,1993,1994
8  *                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
9  *                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au
10  * All rights reserved.
11  *
12  * This copyright notice covers the redistribution and use of the
13  * FPU emulator developed by W. Metzenthen. It covers only its use
14  * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
15  * use is not permitted under this copyright.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must include information specifying
23  *    that source code for the emulator is freely available and include
24  *    either:
25  *      a) an offer to provide the source code for a nominal distribution
26  *         fee, or
27  *      b) list at least two alternative methods whereby the source
28  *         can be obtained, e.g. a publically accessible bulletin board
29  *         and an anonymous ftp site from which the software can be
30  *         downloaded.
31  * 3. All advertising materials specifically mentioning features or use of
32  *    this emulator must acknowledge that it was developed by W. Metzenthen.
33  * 4. The name of W. Metzenthen may not be used to endorse or promote
34  *    products derived from this software without specific prior written
35  *    permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
38  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
39  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
40  * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
41  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
42  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
43  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
44  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
45  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
46  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  *
48  *
49  * The purpose of this copyright, based upon the Berkeley copyright, is to
50  * ensure that the covered software remains freely available to everyone.
51  *
52  * The software (with necessary differences) is also available, but under
53  * the terms of the GNU copyleft, for the Linux operating system and for
54  * the djgpp ms-dos extender.
55  *
56  * W. Metzenthen   June 1994.
57  *
58  *
59  * $FreeBSD: src/sys/gnu/i386/fpemul/poly_l2.c,v 1.10 1999/08/28 00:42:53 peter Exp $
60  *
61  */
62
63
64 #include <gnu/i386/fpemul/reg_constant.h>
65 #include <gnu/i386/fpemul/control_w.h>
66
67
68
69 #define HIPOWER 9
70 static unsigned short lterms[HIPOWER][4] =
71 {
72         /* Ideal computation with these coeffs gives about 64.6 bit rel
73          * accuracy. */
74         {0xe177, 0xb82f, 0x7652, 0x7154},
75         {0xee0f, 0xe80f, 0x2770, 0x7b1c},
76         {0x0fc0, 0xbe87, 0xb143, 0x49dd},
77         {0x78b9, 0xdadd, 0xec54, 0x34c2},
78         {0x003a, 0x5de9, 0x628b, 0x2909},
79         {0x5588, 0xed16, 0x4abf, 0x2193},
80         {0xb461, 0x85f7, 0x347a, 0x1c6a},
81         {0x0975, 0x87b3, 0xd5bf, 0x1876},
82         {0xe85c, 0xcec9, 0x84e7, 0x187d}
83 };
84
85
86
87
88 /*--- poly_l2() -------------------------------------------------------------+
89  |   Base 2 logarithm by a polynomial approximation.                         |
90  +---------------------------------------------------------------------------*/
91 void
92 poly_l2(FPU_REG * arg, FPU_REG * result)
93 {
94         short   exponent;
95         char    zero;           /* flag for an Xx == 0 */
96         unsigned short bits, shift;
97         long long Xsq;
98         FPU_REG accum, denom, num, Xx;
99
100
101         exponent = arg->exp - EXP_BIAS;
102
103         accum.tag = TW_Valid;   /* set the tags to Valid */
104
105         if (arg->sigh > (unsigned) 0xb504f334) {
106                 /* This is good enough for the computation of the polynomial
107                  * sum, but actually results in a loss of precision for the
108                  * computation of Xx. This will matter only if exponent
109                  * becomes zero. */
110                 exponent++;
111                 accum.sign = 1; /* sign to negative */
112                 num.exp = EXP_BIAS;     /* needed to prevent errors in div
113                                          * routine */
114                 reg_u_div(&CONST_1, arg, &num, FULL_PRECISION);
115         } else {
116                 accum.sign = 0; /* set the sign to positive */
117                 num.sigl = arg->sigl;   /* copy the mantissa */
118                 num.sigh = arg->sigh;
119         }
120
121
122         /* shift num left, lose the ms bit */
123         num.sigh <<= 1;
124         if (num.sigl & 0x80000000)
125                 num.sigh |= 1;
126         num.sigl <<= 1;
127
128         denom.sigl = num.sigl;
129         denom.sigh = num.sigh;
130         poly_div4((long long *) &(denom.sigl));
131         denom.sigh += 0x80000000;       /* set the msb */
132         Xx.exp = EXP_BIAS;      /* needed to prevent errors in div routine */
133         reg_u_div(&num, &denom, &Xx, FULL_PRECISION);
134
135         zero = !(Xx.sigh | Xx.sigl);
136
137         mul64((long long *) &Xx.sigl, (long long *) &Xx.sigl, &Xsq);
138         poly_div16(&Xsq);
139
140         accum.exp = -1;         /* exponent of accum */
141
142         /* Do the basic fixed point polynomial evaluation */
143         polynomial((unsigned *) &accum.sigl, (unsigned *) &Xsq, lterms, HIPOWER - 1);
144
145         if (!exponent) {
146                 /* If the exponent is zero, then we would lose precision by
147                  * sticking to fixed point computation here */
148                 /* We need to re-compute Xx because of loss of precision. */
149                 FPU_REG lXx;
150                 char    sign;
151
152                 sign = accum.sign;
153                 accum.sign = 0;
154
155                 /* make accum compatible and normalize */
156                 accum.exp = EXP_BIAS + accum.exp;
157                 normalize(&accum);
158
159                 if (zero) {
160                         reg_move(&CONST_Z, result);
161                 } else {
162                         /* we need to re-compute lXx to better accuracy */
163                         num.tag = TW_Valid;     /* set the tags to Vaild */
164                         num.sign = 0;   /* set the sign to positive */
165                         num.exp = EXP_BIAS - 1;
166                         if (sign) {
167                                 /* The argument is of the form 1-x */
168                                 /* Use  1-1/(1-x) = x/(1-x) */
169                                 *((long long *) &num.sigl) = -*((long long *) &(arg->sigl));
170                                 normalize(&num);
171                                 reg_div(&num, arg, &num, FULL_PRECISION);
172                         } else {
173                                 normalize(&num);
174                         }
175
176                         denom.tag = TW_Valid;   /* set the tags to Valid */
177                         denom.sign = SIGN_POS;  /* set the sign to positive */
178                         denom.exp = EXP_BIAS;
179
180                         reg_div(&num, &denom, &lXx, FULL_PRECISION);
181
182                         reg_u_mul(&lXx, &accum, &accum, FULL_PRECISION);
183
184                         reg_u_add(&lXx, &accum, result, FULL_PRECISION);
185
186                         normalize(result);
187                 }
188
189                 result->sign = sign;
190                 return;
191         }
192         mul64((long long *) &accum.sigl,
193             (long long *) &Xx.sigl, (long long *) &accum.sigl);
194
195         *((long long *) (&accum.sigl)) += *((long long *) (&Xx.sigl));
196
197         if (Xx.sigh > accum.sigh) {
198                 /* There was an overflow */
199
200                 poly_div2((long long *) &accum.sigl);
201                 accum.sigh |= 0x80000000;
202                 accum.exp++;
203         }
204         /* When we add the exponent to the accum result later, we will require
205          * that their signs are the same. Here we ensure that this is so. */
206         if (exponent && ((exponent < 0) ^ (accum.sign))) {
207                 /* signs are different */
208
209                 accum.sign = !accum.sign;
210
211                 /* An exceptional case is when accum is zero */
212                 if (accum.sigl | accum.sigh) {
213                         /* find 1-accum */
214                         /* Shift to get exponent == 0 */
215                         if (accum.exp < 0) {
216                                 poly_div2((long long *) &accum.sigl);
217                                 accum.exp++;
218                         }
219                         /* Just negate, but throw away the sign */
220                         *((long long *) &(accum.sigl)) = -*((long long *) &(accum.sigl));
221                         if (exponent < 0)
222                                 exponent++;
223                         else
224                                 exponent--;
225                 }
226         }
227         shift = exponent >= 0 ? exponent : -exponent;
228         bits = 0;
229         if (shift) {
230                 if (accum.exp) {
231                         accum.exp++;
232                         poly_div2((long long *) &accum.sigl);
233                 }
234                 while (shift) {
235                         poly_div2((long long *) &accum.sigl);
236                         if (shift & 1)
237                                 accum.sigh |= 0x80000000;
238                         shift >>= 1;
239                         bits++;
240                 }
241         }
242         /* Convert to 64 bit signed-compatible */
243         accum.exp += bits + EXP_BIAS - 1;
244
245         reg_move(&accum, result);
246         normalize(result);
247
248         return;
249 }
250
251
252 /*--- poly_l2p1() -----------------------------------------------------------+
253  |   Base 2 logarithm by a polynomial approximation.                         |
254  |   log2(x+1)                                                               |
255  +---------------------------------------------------------------------------*/
256 int
257 poly_l2p1(FPU_REG * arg, FPU_REG * result)
258 {
259         char    sign = 0;
260         long long Xsq;
261         FPU_REG arg_pl1, denom, accum, local_arg, poly_arg;
262
263
264         sign = arg->sign;
265
266         reg_add(arg, &CONST_1, &arg_pl1, FULL_PRECISION);
267
268         if ((arg_pl1.sign) | (arg_pl1.tag)) {   /* We need a valid positive
269                                                  * number! */
270                 return 1;
271         }
272         reg_add(&CONST_1, &arg_pl1, &denom, FULL_PRECISION);
273         reg_div(arg, &denom, &local_arg, FULL_PRECISION);
274         local_arg.sign = 0;     /* Make the sign positive */
275
276         /* Now we need to check that  |local_arg| is less than 3-2*sqrt(2) =
277          * 0.17157.. = .0xafb0ccc0 * 2^-2 */
278
279         if (local_arg.exp >= EXP_BIAS - 3) {
280                 if ((local_arg.exp > EXP_BIAS - 3) ||
281                     (local_arg.sigh > (unsigned) 0xafb0ccc0)) {
282                         /* The argument is large */
283                         poly_l2(&arg_pl1, result);
284                         return 0;
285                 }
286         }
287         /* Make a copy of local_arg */
288         reg_move(&local_arg, &poly_arg);
289
290         /* Get poly_arg bits aligned as required */
291         shrx((unsigned *) &(poly_arg.sigl), -(poly_arg.exp - EXP_BIAS + 3));
292
293         mul64((long long *) &(poly_arg.sigl), (long long *) &(poly_arg.sigl), &Xsq);
294         poly_div16(&Xsq);
295
296         /* Do the basic fixed point polynomial evaluation */
297         polynomial((u_int *) &accum.sigl, (unsigned *) &Xsq, lterms, HIPOWER - 1);
298
299         accum.tag = TW_Valid;   /* set the tags to Valid */
300         accum.sign = SIGN_POS;  /* and make accum positive */
301
302         /* make accum compatible and normalize */
303         accum.exp = EXP_BIAS - 1;
304         normalize(&accum);
305
306         reg_u_mul(&local_arg, &accum, &accum, FULL_PRECISION);
307
308         reg_u_add(&local_arg, &accum, result, FULL_PRECISION);
309
310         /* Multiply the result by 2 */
311         result->exp++;
312
313         result->sign = sign;
314
315         return 0;
316 }