xref: /openbmc/qemu/target/m68k/softfloat.c (revision 719f0f60)
1 /*
2  * Ported from a work by Andreas Grabher for Previous, NeXT Computer Emulator,
3  * derived from NetBSD M68040 FPSP functions,
4  * derived from release 2a of the SoftFloat IEC/IEEE Floating-point Arithmetic
5  * Package. Those parts of the code (and some later contributions) are
6  * provided under that license, as detailed below.
7  * It has subsequently been modified by contributors to the QEMU Project,
8  * so some portions are provided under:
9  *  the SoftFloat-2a license
10  *  the BSD license
11  *  GPL-v2-or-later
12  *
13  * Any future contributions to this file will be taken to be licensed under
14  * the Softfloat-2a license unless specifically indicated otherwise.
15  */
16 
17 /*
18  * Portions of this work are licensed under the terms of the GNU GPL,
19  * version 2 or later. See the COPYING file in the top-level directory.
20  */
21 
22 #include "qemu/osdep.h"
23 #include "softfloat.h"
24 #include "fpu/softfloat-macros.h"
25 #include "softfloat_fpsp_tables.h"
26 
27 #define pi_exp      0x4000
28 #define piby2_exp   0x3FFF
29 #define pi_sig      UINT64_C(0xc90fdaa22168c235)
30 
31 static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status)
32 {
33     if (floatx80_is_signaling_nan(a, status)) {
34         float_raise(float_flag_invalid, status);
35         a = floatx80_silence_nan(a, status);
36     }
37 
38     if (status->default_nan_mode) {
39         return floatx80_default_nan(status);
40     }
41 
42     return a;
43 }
44 
45 /*
46  * Returns the mantissa of the extended double-precision floating-point
47  * value `a'.
48  */
49 
50 floatx80 floatx80_getman(floatx80 a, float_status *status)
51 {
52     bool aSign;
53     int32_t aExp;
54     uint64_t aSig;
55 
56     aSig = extractFloatx80Frac(a);
57     aExp = extractFloatx80Exp(a);
58     aSign = extractFloatx80Sign(a);
59 
60     if (aExp == 0x7FFF) {
61         if ((uint64_t) (aSig << 1)) {
62             return propagateFloatx80NaNOneArg(a , status);
63         }
64         float_raise(float_flag_invalid , status);
65         return floatx80_default_nan(status);
66     }
67 
68     if (aExp == 0) {
69         if (aSig == 0) {
70             return packFloatx80(aSign, 0, 0);
71         }
72         normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
73     }
74 
75     return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign,
76                                 0x3FFF, aSig, 0, status);
77 }
78 
79 /*
80  * Returns the exponent of the extended double-precision floating-point
81  * value `a' as an extended double-precision value.
82  */
83 
84 floatx80 floatx80_getexp(floatx80 a, float_status *status)
85 {
86     bool aSign;
87     int32_t aExp;
88     uint64_t aSig;
89 
90     aSig = extractFloatx80Frac(a);
91     aExp = extractFloatx80Exp(a);
92     aSign = extractFloatx80Sign(a);
93 
94     if (aExp == 0x7FFF) {
95         if ((uint64_t) (aSig << 1)) {
96             return propagateFloatx80NaNOneArg(a , status);
97         }
98         float_raise(float_flag_invalid , status);
99         return floatx80_default_nan(status);
100     }
101 
102     if (aExp == 0) {
103         if (aSig == 0) {
104             return packFloatx80(aSign, 0, 0);
105         }
106         normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
107     }
108 
109     return int32_to_floatx80(aExp - 0x3FFF, status);
110 }
111 
112 /*
113  * Scales extended double-precision floating-point value in operand `a' by
114  * value `b'. The function truncates the value in the second operand 'b' to
115  * an integral value and adds that value to the exponent of the operand 'a'.
116  * The operation performed according to the IEC/IEEE Standard for Binary
117  * Floating-Point Arithmetic.
118  */
119 
120 floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status)
121 {
122     bool aSign, bSign;
123     int32_t aExp, bExp, shiftCount;
124     uint64_t aSig, bSig;
125 
126     aSig = extractFloatx80Frac(a);
127     aExp = extractFloatx80Exp(a);
128     aSign = extractFloatx80Sign(a);
129     bSig = extractFloatx80Frac(b);
130     bExp = extractFloatx80Exp(b);
131     bSign = extractFloatx80Sign(b);
132 
133     if (bExp == 0x7FFF) {
134         if ((uint64_t) (bSig << 1) ||
135             ((aExp == 0x7FFF) && (uint64_t) (aSig << 1))) {
136             return propagateFloatx80NaN(a, b, status);
137         }
138         float_raise(float_flag_invalid , status);
139         return floatx80_default_nan(status);
140     }
141     if (aExp == 0x7FFF) {
142         if ((uint64_t) (aSig << 1)) {
143             return propagateFloatx80NaN(a, b, status);
144         }
145         return packFloatx80(aSign, floatx80_infinity.high,
146                             floatx80_infinity.low);
147     }
148     if (aExp == 0) {
149         if (aSig == 0) {
150             return packFloatx80(aSign, 0, 0);
151         }
152         if (bExp < 0x3FFF) {
153             return a;
154         }
155         normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
156     }
157 
158     if (bExp < 0x3FFF) {
159         return a;
160     }
161 
162     if (0x400F < bExp) {
163         aExp = bSign ? -0x6001 : 0xE000;
164         return roundAndPackFloatx80(status->floatx80_rounding_precision,
165                                     aSign, aExp, aSig, 0, status);
166     }
167 
168     shiftCount = 0x403E - bExp;
169     bSig >>= shiftCount;
170     aExp = bSign ? (aExp - bSig) : (aExp + bSig);
171 
172     return roundAndPackFloatx80(status->floatx80_rounding_precision,
173                                 aSign, aExp, aSig, 0, status);
174 }
175 
176 floatx80 floatx80_move(floatx80 a, float_status *status)
177 {
178     bool aSign;
179     int32_t aExp;
180     uint64_t aSig;
181 
182     aSig = extractFloatx80Frac(a);
183     aExp = extractFloatx80Exp(a);
184     aSign = extractFloatx80Sign(a);
185 
186     if (aExp == 0x7FFF) {
187         if ((uint64_t)(aSig << 1)) {
188             return propagateFloatx80NaNOneArg(a, status);
189         }
190         return a;
191     }
192     if (aExp == 0) {
193         if (aSig == 0) {
194             return a;
195         }
196         normalizeRoundAndPackFloatx80(status->floatx80_rounding_precision,
197                                       aSign, aExp, aSig, 0, status);
198     }
199     return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign,
200                                 aExp, aSig, 0, status);
201 }
202 
203 /*
204  * Algorithms for transcendental functions supported by MC68881 and MC68882
205  * mathematical coprocessors. The functions are derived from FPSP library.
206  */
207 
208 #define one_exp     0x3FFF
209 #define one_sig     UINT64_C(0x8000000000000000)
210 
211 /*
212  * Function for compactifying extended double-precision floating point values.
213  */
214 
215 static int32_t floatx80_make_compact(int32_t aExp, uint64_t aSig)
216 {
217     return (aExp << 16) | (aSig >> 48);
218 }
219 
220 /*
221  * Log base e of x plus 1
222  */
223 
224 floatx80 floatx80_lognp1(floatx80 a, float_status *status)
225 {
226     bool aSign;
227     int32_t aExp;
228     uint64_t aSig, fSig;
229 
230     FloatRoundMode user_rnd_mode;
231     FloatX80RoundPrec user_rnd_prec;
232 
233     int32_t compact, j, k;
234     floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu;
235 
236     aSig = extractFloatx80Frac(a);
237     aExp = extractFloatx80Exp(a);
238     aSign = extractFloatx80Sign(a);
239 
240     if (aExp == 0x7FFF) {
241         if ((uint64_t) (aSig << 1)) {
242             propagateFloatx80NaNOneArg(a, status);
243         }
244         if (aSign) {
245             float_raise(float_flag_invalid, status);
246             return floatx80_default_nan(status);
247         }
248         return packFloatx80(0, floatx80_infinity.high, floatx80_infinity.low);
249     }
250 
251     if (aExp == 0 && aSig == 0) {
252         return packFloatx80(aSign, 0, 0);
253     }
254 
255     if (aSign && aExp >= one_exp) {
256         if (aExp == one_exp && aSig == one_sig) {
257             float_raise(float_flag_divbyzero, status);
258             return packFloatx80(aSign, floatx80_infinity.high,
259                                 floatx80_infinity.low);
260         }
261         float_raise(float_flag_invalid, status);
262         return floatx80_default_nan(status);
263     }
264 
265     if (aExp < 0x3f99 || (aExp == 0x3f99 && aSig == one_sig)) {
266         /* <= min threshold */
267         float_raise(float_flag_inexact, status);
268         return floatx80_move(a, status);
269     }
270 
271     user_rnd_mode = status->float_rounding_mode;
272     user_rnd_prec = status->floatx80_rounding_precision;
273     status->float_rounding_mode = float_round_nearest_even;
274     status->floatx80_rounding_precision = floatx80_precision_x;
275 
276     compact = floatx80_make_compact(aExp, aSig);
277 
278     fp0 = a; /* Z */
279     fp1 = a;
280 
281     fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
282                        status), status); /* X = (1+Z) */
283 
284     aExp = extractFloatx80Exp(fp0);
285     aSig = extractFloatx80Frac(fp0);
286 
287     compact = floatx80_make_compact(aExp, aSig);
288 
289     if (compact < 0x3FFE8000 || compact > 0x3FFFC000) {
290         /* |X| < 1/2 or |X| > 3/2 */
291         k = aExp - 0x3FFF;
292         fp1 = int32_to_floatx80(k, status);
293 
294         fSig = (aSig & UINT64_C(0xFE00000000000000)) | UINT64_C(0x0100000000000000);
295         j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
296 
297         f = packFloatx80(0, 0x3FFF, fSig); /* F */
298         fp0 = packFloatx80(0, 0x3FFF, aSig); /* Y */
299 
300         fp0 = floatx80_sub(fp0, f, status); /* Y-F */
301 
302     lp1cont1:
303         /* LP1CONT1 */
304         fp0 = floatx80_mul(fp0, log_tbl[j], status); /* FP0 IS U = (Y-F)/F */
305         logof2 = packFloatx80(0, 0x3FFE, UINT64_C(0xB17217F7D1CF79AC));
306         klog2 = floatx80_mul(fp1, logof2, status); /* FP1 IS K*LOG2 */
307         fp2 = floatx80_mul(fp0, fp0, status); /* FP2 IS V=U*U */
308 
309         fp3 = fp2;
310         fp1 = fp2;
311 
312         fp1 = floatx80_mul(fp1, float64_to_floatx80(
313                            make_float64(0x3FC2499AB5E4040B), status),
314                            status); /* V*A6 */
315         fp2 = floatx80_mul(fp2, float64_to_floatx80(
316                            make_float64(0xBFC555B5848CB7DB), status),
317                            status); /* V*A5 */
318         fp1 = floatx80_add(fp1, float64_to_floatx80(
319                            make_float64(0x3FC99999987D8730), status),
320                            status); /* A4+V*A6 */
321         fp2 = floatx80_add(fp2, float64_to_floatx80(
322                            make_float64(0xBFCFFFFFFF6F7E97), status),
323                            status); /* A3+V*A5 */
324         fp1 = floatx80_mul(fp1, fp3, status); /* V*(A4+V*A6) */
325         fp2 = floatx80_mul(fp2, fp3, status); /* V*(A3+V*A5) */
326         fp1 = floatx80_add(fp1, float64_to_floatx80(
327                            make_float64(0x3FD55555555555A4), status),
328                            status); /* A2+V*(A4+V*A6) */
329         fp2 = floatx80_add(fp2, float64_to_floatx80(
330                            make_float64(0xBFE0000000000008), status),
331                            status); /* A1+V*(A3+V*A5) */
332         fp1 = floatx80_mul(fp1, fp3, status); /* V*(A2+V*(A4+V*A6)) */
333         fp2 = floatx80_mul(fp2, fp3, status); /* V*(A1+V*(A3+V*A5)) */
334         fp1 = floatx80_mul(fp1, fp0, status); /* U*V*(A2+V*(A4+V*A6)) */
335         fp0 = floatx80_add(fp0, fp2, status); /* U+V*(A1+V*(A3+V*A5)) */
336 
337         fp1 = floatx80_add(fp1, log_tbl[j + 1],
338                            status); /* LOG(F)+U*V*(A2+V*(A4+V*A6)) */
339         fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS LOG(F) + LOG(1+U) */
340 
341         status->float_rounding_mode = user_rnd_mode;
342         status->floatx80_rounding_precision = user_rnd_prec;
343 
344         a = floatx80_add(fp0, klog2, status);
345 
346         float_raise(float_flag_inexact, status);
347 
348         return a;
349     } else if (compact < 0x3FFEF07D || compact > 0x3FFF8841) {
350         /* |X| < 1/16 or |X| > -1/16 */
351         /* LP1CARE */
352         fSig = (aSig & UINT64_C(0xFE00000000000000)) | UINT64_C(0x0100000000000000);
353         f = packFloatx80(0, 0x3FFF, fSig); /* F */
354         j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
355 
356         if (compact >= 0x3FFF8000) { /* 1+Z >= 1 */
357             /* KISZERO */
358             fp0 = floatx80_sub(float32_to_floatx80(make_float32(0x3F800000),
359                                status), f, status); /* 1-F */
360             fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS Y-F = (1-F)+Z */
361             fp1 = packFloatx80(0, 0, 0); /* K = 0 */
362         } else {
363             /* KISNEG */
364             fp0 = floatx80_sub(float32_to_floatx80(make_float32(0x40000000),
365                                status), f, status); /* 2-F */
366             fp1 = floatx80_add(fp1, fp1, status); /* 2Z */
367             fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS Y-F = (2-F)+2Z */
368             fp1 = packFloatx80(1, one_exp, one_sig); /* K = -1 */
369         }
370         goto lp1cont1;
371     } else {
372         /* LP1ONE16 */
373         fp1 = floatx80_add(fp1, fp1, status); /* FP1 IS 2Z */
374         fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
375                            status), status); /* FP0 IS 1+X */
376 
377         /* LP1CONT2 */
378         fp1 = floatx80_div(fp1, fp0, status); /* U */
379         saveu = fp1;
380         fp0 = floatx80_mul(fp1, fp1, status); /* FP0 IS V = U*U */
381         fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS W = V*V */
382 
383         fp3 = float64_to_floatx80(make_float64(0x3F175496ADD7DAD6),
384                                   status); /* B5 */
385         fp2 = float64_to_floatx80(make_float64(0x3F3C71C2FE80C7E0),
386                                   status); /* B4 */
387         fp3 = floatx80_mul(fp3, fp1, status); /* W*B5 */
388         fp2 = floatx80_mul(fp2, fp1, status); /* W*B4 */
389         fp3 = floatx80_add(fp3, float64_to_floatx80(
390                            make_float64(0x3F624924928BCCFF), status),
391                            status); /* B3+W*B5 */
392         fp2 = floatx80_add(fp2, float64_to_floatx80(
393                            make_float64(0x3F899999999995EC), status),
394                            status); /* B2+W*B4 */
395         fp1 = floatx80_mul(fp1, fp3, status); /* W*(B3+W*B5) */
396         fp2 = floatx80_mul(fp2, fp0, status); /* V*(B2+W*B4) */
397         fp1 = floatx80_add(fp1, float64_to_floatx80(
398                            make_float64(0x3FB5555555555555), status),
399                            status); /* B1+W*(B3+W*B5) */
400 
401         fp0 = floatx80_mul(fp0, saveu, status); /* FP0 IS U*V */
402         fp1 = floatx80_add(fp1, fp2,
403                            status); /* B1+W*(B3+W*B5) + V*(B2+W*B4) */
404         fp0 = floatx80_mul(fp0, fp1,
405                            status); /* U*V*([B1+W*(B3+W*B5)] + [V*(B2+W*B4)]) */
406 
407         status->float_rounding_mode = user_rnd_mode;
408         status->floatx80_rounding_precision = user_rnd_prec;
409 
410         a = floatx80_add(fp0, saveu, status);
411 
412         /*if (!floatx80_is_zero(a)) { */
413             float_raise(float_flag_inexact, status);
414         /*} */
415 
416         return a;
417     }
418 }
419 
420 /*
421  * Log base e
422  */
423 
424 floatx80 floatx80_logn(floatx80 a, float_status *status)
425 {
426     bool aSign;
427     int32_t aExp;
428     uint64_t aSig, fSig;
429 
430     FloatRoundMode user_rnd_mode;
431     FloatX80RoundPrec user_rnd_prec;
432 
433     int32_t compact, j, k, adjk;
434     floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu;
435 
436     aSig = extractFloatx80Frac(a);
437     aExp = extractFloatx80Exp(a);
438     aSign = extractFloatx80Sign(a);
439 
440     if (aExp == 0x7FFF) {
441         if ((uint64_t) (aSig << 1)) {
442             propagateFloatx80NaNOneArg(a, status);
443         }
444         if (aSign == 0) {
445             return packFloatx80(0, floatx80_infinity.high,
446                                 floatx80_infinity.low);
447         }
448     }
449 
450     adjk = 0;
451 
452     if (aExp == 0) {
453         if (aSig == 0) { /* zero */
454             float_raise(float_flag_divbyzero, status);
455             return packFloatx80(1, floatx80_infinity.high,
456                                 floatx80_infinity.low);
457         }
458         if ((aSig & one_sig) == 0) { /* denormal */
459             normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
460             adjk = -100;
461             aExp += 100;
462             a = packFloatx80(aSign, aExp, aSig);
463         }
464     }
465 
466     if (aSign) {
467         float_raise(float_flag_invalid, status);
468         return floatx80_default_nan(status);
469     }
470 
471     user_rnd_mode = status->float_rounding_mode;
472     user_rnd_prec = status->floatx80_rounding_precision;
473     status->float_rounding_mode = float_round_nearest_even;
474     status->floatx80_rounding_precision = floatx80_precision_x;
475 
476     compact = floatx80_make_compact(aExp, aSig);
477 
478     if (compact < 0x3FFEF07D || compact > 0x3FFF8841) {
479         /* |X| < 15/16 or |X| > 17/16 */
480         k = aExp - 0x3FFF;
481         k += adjk;
482         fp1 = int32_to_floatx80(k, status);
483 
484         fSig = (aSig & UINT64_C(0xFE00000000000000)) | UINT64_C(0x0100000000000000);
485         j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
486 
487         f = packFloatx80(0, 0x3FFF, fSig); /* F */
488         fp0 = packFloatx80(0, 0x3FFF, aSig); /* Y */
489 
490         fp0 = floatx80_sub(fp0, f, status); /* Y-F */
491 
492         /* LP1CONT1 */
493         fp0 = floatx80_mul(fp0, log_tbl[j], status); /* FP0 IS U = (Y-F)/F */
494         logof2 = packFloatx80(0, 0x3FFE, UINT64_C(0xB17217F7D1CF79AC));
495         klog2 = floatx80_mul(fp1, logof2, status); /* FP1 IS K*LOG2 */
496         fp2 = floatx80_mul(fp0, fp0, status); /* FP2 IS V=U*U */
497 
498         fp3 = fp2;
499         fp1 = fp2;
500 
501         fp1 = floatx80_mul(fp1, float64_to_floatx80(
502                            make_float64(0x3FC2499AB5E4040B), status),
503                            status); /* V*A6 */
504         fp2 = floatx80_mul(fp2, float64_to_floatx80(
505                            make_float64(0xBFC555B5848CB7DB), status),
506                            status); /* V*A5 */
507         fp1 = floatx80_add(fp1, float64_to_floatx80(
508                            make_float64(0x3FC99999987D8730), status),
509                            status); /* A4+V*A6 */
510         fp2 = floatx80_add(fp2, float64_to_floatx80(
511                            make_float64(0xBFCFFFFFFF6F7E97), status),
512                            status); /* A3+V*A5 */
513         fp1 = floatx80_mul(fp1, fp3, status); /* V*(A4+V*A6) */
514         fp2 = floatx80_mul(fp2, fp3, status); /* V*(A3+V*A5) */
515         fp1 = floatx80_add(fp1, float64_to_floatx80(
516                            make_float64(0x3FD55555555555A4), status),
517                            status); /* A2+V*(A4+V*A6) */
518         fp2 = floatx80_add(fp2, float64_to_floatx80(
519                            make_float64(0xBFE0000000000008), status),
520                            status); /* A1+V*(A3+V*A5) */
521         fp1 = floatx80_mul(fp1, fp3, status); /* V*(A2+V*(A4+V*A6)) */
522         fp2 = floatx80_mul(fp2, fp3, status); /* V*(A1+V*(A3+V*A5)) */
523         fp1 = floatx80_mul(fp1, fp0, status); /* U*V*(A2+V*(A4+V*A6)) */
524         fp0 = floatx80_add(fp0, fp2, status); /* U+V*(A1+V*(A3+V*A5)) */
525 
526         fp1 = floatx80_add(fp1, log_tbl[j + 1],
527                            status); /* LOG(F)+U*V*(A2+V*(A4+V*A6)) */
528         fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS LOG(F) + LOG(1+U) */
529 
530         status->float_rounding_mode = user_rnd_mode;
531         status->floatx80_rounding_precision = user_rnd_prec;
532 
533         a = floatx80_add(fp0, klog2, status);
534 
535         float_raise(float_flag_inexact, status);
536 
537         return a;
538     } else { /* |X-1| >= 1/16 */
539         fp0 = a;
540         fp1 = a;
541         fp1 = floatx80_sub(fp1, float32_to_floatx80(make_float32(0x3F800000),
542                            status), status); /* FP1 IS X-1 */
543         fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
544                            status), status); /* FP0 IS X+1 */
545         fp1 = floatx80_add(fp1, fp1, status); /* FP1 IS 2(X-1) */
546 
547         /* LP1CONT2 */
548         fp1 = floatx80_div(fp1, fp0, status); /* U */
549         saveu = fp1;
550         fp0 = floatx80_mul(fp1, fp1, status); /* FP0 IS V = U*U */
551         fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS W = V*V */
552 
553         fp3 = float64_to_floatx80(make_float64(0x3F175496ADD7DAD6),
554                                   status); /* B5 */
555         fp2 = float64_to_floatx80(make_float64(0x3F3C71C2FE80C7E0),
556                                   status); /* B4 */
557         fp3 = floatx80_mul(fp3, fp1, status); /* W*B5 */
558         fp2 = floatx80_mul(fp2, fp1, status); /* W*B4 */
559         fp3 = floatx80_add(fp3, float64_to_floatx80(
560                            make_float64(0x3F624924928BCCFF), status),
561                            status); /* B3+W*B5 */
562         fp2 = floatx80_add(fp2, float64_to_floatx80(
563                            make_float64(0x3F899999999995EC), status),
564                            status); /* B2+W*B4 */
565         fp1 = floatx80_mul(fp1, fp3, status); /* W*(B3+W*B5) */
566         fp2 = floatx80_mul(fp2, fp0, status); /* V*(B2+W*B4) */
567         fp1 = floatx80_add(fp1, float64_to_floatx80(
568                            make_float64(0x3FB5555555555555), status),
569                            status); /* B1+W*(B3+W*B5) */
570 
571         fp0 = floatx80_mul(fp0, saveu, status); /* FP0 IS U*V */
572         fp1 = floatx80_add(fp1, fp2, status); /* B1+W*(B3+W*B5) + V*(B2+W*B4) */
573         fp0 = floatx80_mul(fp0, fp1,
574                            status); /* U*V*([B1+W*(B3+W*B5)] + [V*(B2+W*B4)]) */
575 
576         status->float_rounding_mode = user_rnd_mode;
577         status->floatx80_rounding_precision = user_rnd_prec;
578 
579         a = floatx80_add(fp0, saveu, status);
580 
581         /*if (!floatx80_is_zero(a)) { */
582             float_raise(float_flag_inexact, status);
583         /*} */
584 
585         return a;
586     }
587 }
588 
589 /*
590  * Log base 10
591  */
592 
593 floatx80 floatx80_log10(floatx80 a, float_status *status)
594 {
595     bool aSign;
596     int32_t aExp;
597     uint64_t aSig;
598 
599     FloatRoundMode user_rnd_mode;
600     FloatX80RoundPrec user_rnd_prec;
601 
602     floatx80 fp0, fp1;
603 
604     aSig = extractFloatx80Frac(a);
605     aExp = extractFloatx80Exp(a);
606     aSign = extractFloatx80Sign(a);
607 
608     if (aExp == 0x7FFF) {
609         if ((uint64_t) (aSig << 1)) {
610             propagateFloatx80NaNOneArg(a, status);
611         }
612         if (aSign == 0) {
613             return packFloatx80(0, floatx80_infinity.high,
614                                 floatx80_infinity.low);
615         }
616     }
617 
618     if (aExp == 0 && aSig == 0) {
619         float_raise(float_flag_divbyzero, status);
620         return packFloatx80(1, floatx80_infinity.high,
621                             floatx80_infinity.low);
622     }
623 
624     if (aSign) {
625         float_raise(float_flag_invalid, status);
626         return floatx80_default_nan(status);
627     }
628 
629     user_rnd_mode = status->float_rounding_mode;
630     user_rnd_prec = status->floatx80_rounding_precision;
631     status->float_rounding_mode = float_round_nearest_even;
632     status->floatx80_rounding_precision = floatx80_precision_x;
633 
634     fp0 = floatx80_logn(a, status);
635     fp1 = packFloatx80(0, 0x3FFD, UINT64_C(0xDE5BD8A937287195)); /* INV_L10 */
636 
637     status->float_rounding_mode = user_rnd_mode;
638     status->floatx80_rounding_precision = user_rnd_prec;
639 
640     a = floatx80_mul(fp0, fp1, status); /* LOGN(X)*INV_L10 */
641 
642     float_raise(float_flag_inexact, status);
643 
644     return a;
645 }
646 
647 /*
648  * Log base 2
649  */
650 
651 floatx80 floatx80_log2(floatx80 a, float_status *status)
652 {
653     bool aSign;
654     int32_t aExp;
655     uint64_t aSig;
656 
657     FloatRoundMode user_rnd_mode;
658     FloatX80RoundPrec user_rnd_prec;
659 
660     floatx80 fp0, fp1;
661 
662     aSig = extractFloatx80Frac(a);
663     aExp = extractFloatx80Exp(a);
664     aSign = extractFloatx80Sign(a);
665 
666     if (aExp == 0x7FFF) {
667         if ((uint64_t) (aSig << 1)) {
668             propagateFloatx80NaNOneArg(a, status);
669         }
670         if (aSign == 0) {
671             return packFloatx80(0, floatx80_infinity.high,
672                                 floatx80_infinity.low);
673         }
674     }
675 
676     if (aExp == 0) {
677         if (aSig == 0) {
678             float_raise(float_flag_divbyzero, status);
679             return packFloatx80(1, floatx80_infinity.high,
680                                 floatx80_infinity.low);
681         }
682         normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
683     }
684 
685     if (aSign) {
686         float_raise(float_flag_invalid, status);
687         return floatx80_default_nan(status);
688     }
689 
690     user_rnd_mode = status->float_rounding_mode;
691     user_rnd_prec = status->floatx80_rounding_precision;
692     status->float_rounding_mode = float_round_nearest_even;
693     status->floatx80_rounding_precision = floatx80_precision_x;
694 
695     if (aSig == one_sig) { /* X is 2^k */
696         status->float_rounding_mode = user_rnd_mode;
697         status->floatx80_rounding_precision = user_rnd_prec;
698 
699         a = int32_to_floatx80(aExp - 0x3FFF, status);
700     } else {
701         fp0 = floatx80_logn(a, status);
702         fp1 = packFloatx80(0, 0x3FFF, UINT64_C(0xB8AA3B295C17F0BC)); /* INV_L2 */
703 
704         status->float_rounding_mode = user_rnd_mode;
705         status->floatx80_rounding_precision = user_rnd_prec;
706 
707         a = floatx80_mul(fp0, fp1, status); /* LOGN(X)*INV_L2 */
708     }
709 
710     float_raise(float_flag_inexact, status);
711 
712     return a;
713 }
714 
715 /*
716  * e to x
717  */
718 
719 floatx80 floatx80_etox(floatx80 a, float_status *status)
720 {
721     bool aSign;
722     int32_t aExp;
723     uint64_t aSig;
724 
725     FloatRoundMode user_rnd_mode;
726     FloatX80RoundPrec user_rnd_prec;
727 
728     int32_t compact, n, j, k, m, m1;
729     floatx80 fp0, fp1, fp2, fp3, l2, scale, adjscale;
730     bool adjflag;
731 
732     aSig = extractFloatx80Frac(a);
733     aExp = extractFloatx80Exp(a);
734     aSign = extractFloatx80Sign(a);
735 
736     if (aExp == 0x7FFF) {
737         if ((uint64_t) (aSig << 1)) {
738             return propagateFloatx80NaNOneArg(a, status);
739         }
740         if (aSign) {
741             return packFloatx80(0, 0, 0);
742         }
743         return packFloatx80(0, floatx80_infinity.high,
744                             floatx80_infinity.low);
745     }
746 
747     if (aExp == 0 && aSig == 0) {
748         return packFloatx80(0, one_exp, one_sig);
749     }
750 
751     user_rnd_mode = status->float_rounding_mode;
752     user_rnd_prec = status->floatx80_rounding_precision;
753     status->float_rounding_mode = float_round_nearest_even;
754     status->floatx80_rounding_precision = floatx80_precision_x;
755 
756     adjflag = 0;
757 
758     if (aExp >= 0x3FBE) { /* |X| >= 2^(-65) */
759         compact = floatx80_make_compact(aExp, aSig);
760 
761         if (compact < 0x400CB167) { /* |X| < 16380 log2 */
762             fp0 = a;
763             fp1 = a;
764             fp0 = floatx80_mul(fp0, float32_to_floatx80(
765                                make_float32(0x42B8AA3B), status),
766                                status); /* 64/log2 * X */
767             adjflag = 0;
768             n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */
769             fp0 = int32_to_floatx80(n, status);
770 
771             j = n & 0x3F; /* J = N mod 64 */
772             m = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
773             if (n < 0 && j) {
774                 /*
775                  * arithmetic right shift is division and
776                  * round towards minus infinity
777                  */
778                 m--;
779             }
780             m += 0x3FFF; /* biased exponent of 2^(M) */
781 
782         expcont1:
783             fp2 = fp0; /* N */
784             fp0 = floatx80_mul(fp0, float32_to_floatx80(
785                                make_float32(0xBC317218), status),
786                                status); /* N * L1, L1 = lead(-log2/64) */
787             l2 = packFloatx80(0, 0x3FDC, UINT64_C(0x82E308654361C4C6));
788             fp2 = floatx80_mul(fp2, l2, status); /* N * L2, L1+L2 = -log2/64 */
789             fp0 = floatx80_add(fp0, fp1, status); /* X + N*L1 */
790             fp0 = floatx80_add(fp0, fp2, status); /* R */
791 
792             fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
793             fp2 = float32_to_floatx80(make_float32(0x3AB60B70),
794                                       status); /* A5 */
795             fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*A5 */
796             fp3 = floatx80_mul(float32_to_floatx80(make_float32(0x3C088895),
797                                status), fp1,
798                                status); /* fp3 is S*A4 */
799             fp2 = floatx80_add(fp2, float64_to_floatx80(make_float64(
800                                0x3FA5555555554431), status),
801                                status); /* fp2 is A3+S*A5 */
802             fp3 = floatx80_add(fp3, float64_to_floatx80(make_float64(
803                                0x3FC5555555554018), status),
804                                status); /* fp3 is A2+S*A4 */
805             fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*(A3+S*A5) */
806             fp3 = floatx80_mul(fp3, fp1, status); /* fp3 is S*(A2+S*A4) */
807             fp2 = floatx80_add(fp2, float32_to_floatx80(
808                                make_float32(0x3F000000), status),
809                                status); /* fp2 is A1+S*(A3+S*A5) */
810             fp3 = floatx80_mul(fp3, fp0, status); /* fp3 IS R*S*(A2+S*A4) */
811             fp2 = floatx80_mul(fp2, fp1,
812                                status); /* fp2 IS S*(A1+S*(A3+S*A5)) */
813             fp0 = floatx80_add(fp0, fp3, status); /* fp0 IS R+R*S*(A2+S*A4) */
814             fp0 = floatx80_add(fp0, fp2, status); /* fp0 IS EXP(R) - 1 */
815 
816             fp1 = exp_tbl[j];
817             fp0 = floatx80_mul(fp0, fp1, status); /* 2^(J/64)*(Exp(R)-1) */
818             fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], status),
819                                status); /* accurate 2^(J/64) */
820             fp0 = floatx80_add(fp0, fp1,
821                                status); /* 2^(J/64) + 2^(J/64)*(Exp(R)-1) */
822 
823             scale = packFloatx80(0, m, one_sig);
824             if (adjflag) {
825                 adjscale = packFloatx80(0, m1, one_sig);
826                 fp0 = floatx80_mul(fp0, adjscale, status);
827             }
828 
829             status->float_rounding_mode = user_rnd_mode;
830             status->floatx80_rounding_precision = user_rnd_prec;
831 
832             a = floatx80_mul(fp0, scale, status);
833 
834             float_raise(float_flag_inexact, status);
835 
836             return a;
837         } else { /* |X| >= 16380 log2 */
838             if (compact > 0x400CB27C) { /* |X| >= 16480 log2 */
839                 status->float_rounding_mode = user_rnd_mode;
840                 status->floatx80_rounding_precision = user_rnd_prec;
841                 if (aSign) {
842                     a = roundAndPackFloatx80(
843                                            status->floatx80_rounding_precision,
844                                            0, -0x1000, aSig, 0, status);
845                 } else {
846                     a = roundAndPackFloatx80(
847                                            status->floatx80_rounding_precision,
848                                            0, 0x8000, aSig, 0, status);
849                 }
850                 float_raise(float_flag_inexact, status);
851 
852                 return a;
853             } else {
854                 fp0 = a;
855                 fp1 = a;
856                 fp0 = floatx80_mul(fp0, float32_to_floatx80(
857                                    make_float32(0x42B8AA3B), status),
858                                    status); /* 64/log2 * X */
859                 adjflag = 1;
860                 n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */
861                 fp0 = int32_to_floatx80(n, status);
862 
863                 j = n & 0x3F; /* J = N mod 64 */
864                 /* NOTE: this is really arithmetic right shift by 6 */
865                 k = n / 64;
866                 if (n < 0 && j) {
867                     /* arithmetic right shift is division and
868                      * round towards minus infinity
869                      */
870                     k--;
871                 }
872                 /* NOTE: this is really arithmetic right shift by 1 */
873                 m1 = k / 2;
874                 if (k < 0 && (k & 1)) {
875                     /* arithmetic right shift is division and
876                      * round towards minus infinity
877                      */
878                     m1--;
879                 }
880                 m = k - m1;
881                 m1 += 0x3FFF; /* biased exponent of 2^(M1) */
882                 m += 0x3FFF; /* biased exponent of 2^(M) */
883 
884                 goto expcont1;
885             }
886         }
887     } else { /* |X| < 2^(-65) */
888         status->float_rounding_mode = user_rnd_mode;
889         status->floatx80_rounding_precision = user_rnd_prec;
890 
891         a = floatx80_add(a, float32_to_floatx80(make_float32(0x3F800000),
892                          status), status); /* 1 + X */
893 
894         float_raise(float_flag_inexact, status);
895 
896         return a;
897     }
898 }
899 
900 /*
901  * 2 to x
902  */
903 
904 floatx80 floatx80_twotox(floatx80 a, float_status *status)
905 {
906     bool aSign;
907     int32_t aExp;
908     uint64_t aSig;
909 
910     FloatRoundMode user_rnd_mode;
911     FloatX80RoundPrec user_rnd_prec;
912 
913     int32_t compact, n, j, l, m, m1;
914     floatx80 fp0, fp1, fp2, fp3, adjfact, fact1, fact2;
915 
916     aSig = extractFloatx80Frac(a);
917     aExp = extractFloatx80Exp(a);
918     aSign = extractFloatx80Sign(a);
919 
920     if (aExp == 0x7FFF) {
921         if ((uint64_t) (aSig << 1)) {
922             return propagateFloatx80NaNOneArg(a, status);
923         }
924         if (aSign) {
925             return packFloatx80(0, 0, 0);
926         }
927         return packFloatx80(0, floatx80_infinity.high,
928                             floatx80_infinity.low);
929     }
930 
931     if (aExp == 0 && aSig == 0) {
932         return packFloatx80(0, one_exp, one_sig);
933     }
934 
935     user_rnd_mode = status->float_rounding_mode;
936     user_rnd_prec = status->floatx80_rounding_precision;
937     status->float_rounding_mode = float_round_nearest_even;
938     status->floatx80_rounding_precision = floatx80_precision_x;
939 
940     fp0 = a;
941 
942     compact = floatx80_make_compact(aExp, aSig);
943 
944     if (compact < 0x3FB98000 || compact > 0x400D80C0) {
945         /* |X| > 16480 or |X| < 2^(-70) */
946         if (compact > 0x3FFF8000) { /* |X| > 16480 */
947             status->float_rounding_mode = user_rnd_mode;
948             status->floatx80_rounding_precision = user_rnd_prec;
949 
950             if (aSign) {
951                 return roundAndPackFloatx80(status->floatx80_rounding_precision,
952                                             0, -0x1000, aSig, 0, status);
953             } else {
954                 return roundAndPackFloatx80(status->floatx80_rounding_precision,
955                                             0, 0x8000, aSig, 0, status);
956             }
957         } else { /* |X| < 2^(-70) */
958             status->float_rounding_mode = user_rnd_mode;
959             status->floatx80_rounding_precision = user_rnd_prec;
960 
961             a = floatx80_add(fp0, float32_to_floatx80(
962                              make_float32(0x3F800000), status),
963                              status); /* 1 + X */
964 
965             float_raise(float_flag_inexact, status);
966 
967             return a;
968         }
969     } else { /* 2^(-70) <= |X| <= 16480 */
970         fp1 = fp0; /* X */
971         fp1 = floatx80_mul(fp1, float32_to_floatx80(
972                            make_float32(0x42800000), status),
973                            status); /* X * 64 */
974         n = floatx80_to_int32(fp1, status);
975         fp1 = int32_to_floatx80(n, status);
976         j = n & 0x3F;
977         l = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
978         if (n < 0 && j) {
979             /*
980              * arithmetic right shift is division and
981              * round towards minus infinity
982              */
983             l--;
984         }
985         m = l / 2; /* NOTE: this is really arithmetic right shift by 1 */
986         if (l < 0 && (l & 1)) {
987             /*
988              * arithmetic right shift is division and
989              * round towards minus infinity
990              */
991             m--;
992         }
993         m1 = l - m;
994         m1 += 0x3FFF; /* ADJFACT IS 2^(M') */
995 
996         adjfact = packFloatx80(0, m1, one_sig);
997         fact1 = exp2_tbl[j];
998         fact1.high += m;
999         fact2.high = exp2_tbl2[j] >> 16;
1000         fact2.high += m;
1001         fact2.low = (uint64_t)(exp2_tbl2[j] & 0xFFFF);
1002         fact2.low <<= 48;
1003 
1004         fp1 = floatx80_mul(fp1, float32_to_floatx80(
1005                            make_float32(0x3C800000), status),
1006                            status); /* (1/64)*N */
1007         fp0 = floatx80_sub(fp0, fp1, status); /* X - (1/64)*INT(64 X) */
1008         fp2 = packFloatx80(0, 0x3FFE, UINT64_C(0xB17217F7D1CF79AC)); /* LOG2 */
1009         fp0 = floatx80_mul(fp0, fp2, status); /* R */
1010 
1011         /* EXPR */
1012         fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
1013         fp2 = float64_to_floatx80(make_float64(0x3F56C16D6F7BD0B2),
1014                                   status); /* A5 */
1015         fp3 = float64_to_floatx80(make_float64(0x3F811112302C712C),
1016                                   status); /* A4 */
1017         fp2 = floatx80_mul(fp2, fp1, status); /* S*A5 */
1018         fp3 = floatx80_mul(fp3, fp1, status); /* S*A4 */
1019         fp2 = floatx80_add(fp2, float64_to_floatx80(
1020                            make_float64(0x3FA5555555554CC1), status),
1021                            status); /* A3+S*A5 */
1022         fp3 = floatx80_add(fp3, float64_to_floatx80(
1023                            make_float64(0x3FC5555555554A54), status),
1024                            status); /* A2+S*A4 */
1025         fp2 = floatx80_mul(fp2, fp1, status); /* S*(A3+S*A5) */
1026         fp3 = floatx80_mul(fp3, fp1, status); /* S*(A2+S*A4) */
1027         fp2 = floatx80_add(fp2, float64_to_floatx80(
1028                            make_float64(0x3FE0000000000000), status),
1029                            status); /* A1+S*(A3+S*A5) */
1030         fp3 = floatx80_mul(fp3, fp0, status); /* R*S*(A2+S*A4) */
1031 
1032         fp2 = floatx80_mul(fp2, fp1, status); /* S*(A1+S*(A3+S*A5)) */
1033         fp0 = floatx80_add(fp0, fp3, status); /* R+R*S*(A2+S*A4) */
1034         fp0 = floatx80_add(fp0, fp2, status); /* EXP(R) - 1 */
1035 
1036         fp0 = floatx80_mul(fp0, fact1, status);
1037         fp0 = floatx80_add(fp0, fact2, status);
1038         fp0 = floatx80_add(fp0, fact1, status);
1039 
1040         status->float_rounding_mode = user_rnd_mode;
1041         status->floatx80_rounding_precision = user_rnd_prec;
1042 
1043         a = floatx80_mul(fp0, adjfact, status);
1044 
1045         float_raise(float_flag_inexact, status);
1046 
1047         return a;
1048     }
1049 }
1050 
1051 /*
1052  * 10 to x
1053  */
1054 
1055 floatx80 floatx80_tentox(floatx80 a, float_status *status)
1056 {
1057     bool aSign;
1058     int32_t aExp;
1059     uint64_t aSig;
1060 
1061     FloatRoundMode user_rnd_mode;
1062     FloatX80RoundPrec user_rnd_prec;
1063 
1064     int32_t compact, n, j, l, m, m1;
1065     floatx80 fp0, fp1, fp2, fp3, adjfact, fact1, fact2;
1066 
1067     aSig = extractFloatx80Frac(a);
1068     aExp = extractFloatx80Exp(a);
1069     aSign = extractFloatx80Sign(a);
1070 
1071     if (aExp == 0x7FFF) {
1072         if ((uint64_t) (aSig << 1)) {
1073             return propagateFloatx80NaNOneArg(a, status);
1074         }
1075         if (aSign) {
1076             return packFloatx80(0, 0, 0);
1077         }
1078         return packFloatx80(0, floatx80_infinity.high,
1079                             floatx80_infinity.low);
1080     }
1081 
1082     if (aExp == 0 && aSig == 0) {
1083         return packFloatx80(0, one_exp, one_sig);
1084     }
1085 
1086     user_rnd_mode = status->float_rounding_mode;
1087     user_rnd_prec = status->floatx80_rounding_precision;
1088     status->float_rounding_mode = float_round_nearest_even;
1089     status->floatx80_rounding_precision = floatx80_precision_x;
1090 
1091     fp0 = a;
1092 
1093     compact = floatx80_make_compact(aExp, aSig);
1094 
1095     if (compact < 0x3FB98000 || compact > 0x400B9B07) {
1096         /* |X| > 16480 LOG2/LOG10 or |X| < 2^(-70) */
1097         if (compact > 0x3FFF8000) { /* |X| > 16480 */
1098             status->float_rounding_mode = user_rnd_mode;
1099             status->floatx80_rounding_precision = user_rnd_prec;
1100 
1101             if (aSign) {
1102                 return roundAndPackFloatx80(status->floatx80_rounding_precision,
1103                                             0, -0x1000, aSig, 0, status);
1104             } else {
1105                 return roundAndPackFloatx80(status->floatx80_rounding_precision,
1106                                             0, 0x8000, aSig, 0, status);
1107             }
1108         } else { /* |X| < 2^(-70) */
1109             status->float_rounding_mode = user_rnd_mode;
1110             status->floatx80_rounding_precision = user_rnd_prec;
1111 
1112             a = floatx80_add(fp0, float32_to_floatx80(
1113                              make_float32(0x3F800000), status),
1114                              status); /* 1 + X */
1115 
1116             float_raise(float_flag_inexact, status);
1117 
1118             return a;
1119         }
1120     } else { /* 2^(-70) <= |X| <= 16480 LOG 2 / LOG 10 */
1121         fp1 = fp0; /* X */
1122         fp1 = floatx80_mul(fp1, float64_to_floatx80(
1123                            make_float64(0x406A934F0979A371),
1124                            status), status); /* X*64*LOG10/LOG2 */
1125         n = floatx80_to_int32(fp1, status); /* N=INT(X*64*LOG10/LOG2) */
1126         fp1 = int32_to_floatx80(n, status);
1127 
1128         j = n & 0x3F;
1129         l = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
1130         if (n < 0 && j) {
1131             /*
1132              * arithmetic right shift is division and
1133              * round towards minus infinity
1134              */
1135             l--;
1136         }
1137         m = l / 2; /* NOTE: this is really arithmetic right shift by 1 */
1138         if (l < 0 && (l & 1)) {
1139             /*
1140              * arithmetic right shift is division and
1141              * round towards minus infinity
1142              */
1143             m--;
1144         }
1145         m1 = l - m;
1146         m1 += 0x3FFF; /* ADJFACT IS 2^(M') */
1147 
1148         adjfact = packFloatx80(0, m1, one_sig);
1149         fact1 = exp2_tbl[j];
1150         fact1.high += m;
1151         fact2.high = exp2_tbl2[j] >> 16;
1152         fact2.high += m;
1153         fact2.low = (uint64_t)(exp2_tbl2[j] & 0xFFFF);
1154         fact2.low <<= 48;
1155 
1156         fp2 = fp1; /* N */
1157         fp1 = floatx80_mul(fp1, float64_to_floatx80(
1158                            make_float64(0x3F734413509F8000), status),
1159                            status); /* N*(LOG2/64LOG10)_LEAD */
1160         fp3 = packFloatx80(1, 0x3FCD, UINT64_C(0xC0219DC1DA994FD2));
1161         fp2 = floatx80_mul(fp2, fp3, status); /* N*(LOG2/64LOG10)_TRAIL */
1162         fp0 = floatx80_sub(fp0, fp1, status); /* X - N L_LEAD */
1163         fp0 = floatx80_sub(fp0, fp2, status); /* X - N L_TRAIL */
1164         fp2 = packFloatx80(0, 0x4000, UINT64_C(0x935D8DDDAAA8AC17)); /* LOG10 */
1165         fp0 = floatx80_mul(fp0, fp2, status); /* R */
1166 
1167         /* EXPR */
1168         fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
1169         fp2 = float64_to_floatx80(make_float64(0x3F56C16D6F7BD0B2),
1170                                   status); /* A5 */
1171         fp3 = float64_to_floatx80(make_float64(0x3F811112302C712C),
1172                                   status); /* A4 */
1173         fp2 = floatx80_mul(fp2, fp1, status); /* S*A5 */
1174         fp3 = floatx80_mul(fp3, fp1, status); /* S*A4 */
1175         fp2 = floatx80_add(fp2, float64_to_floatx80(
1176                            make_float64(0x3FA5555555554CC1), status),
1177                            status); /* A3+S*A5 */
1178         fp3 = floatx80_add(fp3, float64_to_floatx80(
1179                            make_float64(0x3FC5555555554A54), status),
1180                            status); /* A2+S*A4 */
1181         fp2 = floatx80_mul(fp2, fp1, status); /* S*(A3+S*A5) */
1182         fp3 = floatx80_mul(fp3, fp1, status); /* S*(A2+S*A4) */
1183         fp2 = floatx80_add(fp2, float64_to_floatx80(
1184                            make_float64(0x3FE0000000000000), status),
1185                            status); /* A1+S*(A3+S*A5) */
1186         fp3 = floatx80_mul(fp3, fp0, status); /* R*S*(A2+S*A4) */
1187 
1188         fp2 = floatx80_mul(fp2, fp1, status); /* S*(A1+S*(A3+S*A5)) */
1189         fp0 = floatx80_add(fp0, fp3, status); /* R+R*S*(A2+S*A4) */
1190         fp0 = floatx80_add(fp0, fp2, status); /* EXP(R) - 1 */
1191 
1192         fp0 = floatx80_mul(fp0, fact1, status);
1193         fp0 = floatx80_add(fp0, fact2, status);
1194         fp0 = floatx80_add(fp0, fact1, status);
1195 
1196         status->float_rounding_mode = user_rnd_mode;
1197         status->floatx80_rounding_precision = user_rnd_prec;
1198 
1199         a = floatx80_mul(fp0, adjfact, status);
1200 
1201         float_raise(float_flag_inexact, status);
1202 
1203         return a;
1204     }
1205 }
1206 
1207 /*
1208  * Tangent
1209  */
1210 
1211 floatx80 floatx80_tan(floatx80 a, float_status *status)
1212 {
1213     bool aSign, xSign;
1214     int32_t aExp, xExp;
1215     uint64_t aSig, xSig;
1216 
1217     FloatRoundMode user_rnd_mode;
1218     FloatX80RoundPrec user_rnd_prec;
1219 
1220     int32_t compact, l, n, j;
1221     floatx80 fp0, fp1, fp2, fp3, fp4, fp5, invtwopi, twopi1, twopi2;
1222     float32 twoto63;
1223     bool endflag;
1224 
1225     aSig = extractFloatx80Frac(a);
1226     aExp = extractFloatx80Exp(a);
1227     aSign = extractFloatx80Sign(a);
1228 
1229     if (aExp == 0x7FFF) {
1230         if ((uint64_t) (aSig << 1)) {
1231             return propagateFloatx80NaNOneArg(a, status);
1232         }
1233         float_raise(float_flag_invalid, status);
1234         return floatx80_default_nan(status);
1235     }
1236 
1237     if (aExp == 0 && aSig == 0) {
1238         return packFloatx80(aSign, 0, 0);
1239     }
1240 
1241     user_rnd_mode = status->float_rounding_mode;
1242     user_rnd_prec = status->floatx80_rounding_precision;
1243     status->float_rounding_mode = float_round_nearest_even;
1244     status->floatx80_rounding_precision = floatx80_precision_x;
1245 
1246     compact = floatx80_make_compact(aExp, aSig);
1247 
1248     fp0 = a;
1249 
1250     if (compact < 0x3FD78000 || compact > 0x4004BC7E) {
1251         /* 2^(-40) > |X| > 15 PI */
1252         if (compact > 0x3FFF8000) { /* |X| >= 15 PI */
1253             /* REDUCEX */
1254             fp1 = packFloatx80(0, 0, 0);
1255             if (compact == 0x7FFEFFFF) {
1256                 twopi1 = packFloatx80(aSign ^ 1, 0x7FFE,
1257                                       UINT64_C(0xC90FDAA200000000));
1258                 twopi2 = packFloatx80(aSign ^ 1, 0x7FDC,
1259                                       UINT64_C(0x85A308D300000000));
1260                 fp0 = floatx80_add(fp0, twopi1, status);
1261                 fp1 = fp0;
1262                 fp0 = floatx80_add(fp0, twopi2, status);
1263                 fp1 = floatx80_sub(fp1, fp0, status);
1264                 fp1 = floatx80_add(fp1, twopi2, status);
1265             }
1266         loop:
1267             xSign = extractFloatx80Sign(fp0);
1268             xExp = extractFloatx80Exp(fp0);
1269             xExp -= 0x3FFF;
1270             if (xExp <= 28) {
1271                 l = 0;
1272                 endflag = true;
1273             } else {
1274                 l = xExp - 27;
1275                 endflag = false;
1276             }
1277             invtwopi = packFloatx80(0, 0x3FFE - l,
1278                                     UINT64_C(0xA2F9836E4E44152A)); /* INVTWOPI */
1279             twopi1 = packFloatx80(0, 0x3FFF + l, UINT64_C(0xC90FDAA200000000));
1280             twopi2 = packFloatx80(0, 0x3FDD + l, UINT64_C(0x85A308D300000000));
1281 
1282             /* SIGN(INARG)*2^63 IN SGL */
1283             twoto63 = packFloat32(xSign, 0xBE, 0);
1284 
1285             fp2 = floatx80_mul(fp0, invtwopi, status);
1286             fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status),
1287                                status); /* THE FRACT PART OF FP2 IS ROUNDED */
1288             fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status),
1289                                status); /* FP2 is N */
1290             fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */
1291             fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */
1292             fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */
1293             fp4 = floatx80_sub(fp4, fp3, status); /* W-P */
1294             fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */
1295             fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */
1296             fp3 = fp0; /* FP3 is A */
1297             fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */
1298             fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */
1299 
1300             if (endflag) {
1301                 n = floatx80_to_int32(fp2, status);
1302                 goto tancont;
1303             }
1304             fp3 = floatx80_sub(fp3, fp0, status); /* A-R */
1305             fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */
1306             goto loop;
1307         } else {
1308             status->float_rounding_mode = user_rnd_mode;
1309             status->floatx80_rounding_precision = user_rnd_prec;
1310 
1311             a = floatx80_move(a, status);
1312 
1313             float_raise(float_flag_inexact, status);
1314 
1315             return a;
1316         }
1317     } else {
1318         fp1 = floatx80_mul(fp0, float64_to_floatx80(
1319                            make_float64(0x3FE45F306DC9C883), status),
1320                            status); /* X*2/PI */
1321 
1322         n = floatx80_to_int32(fp1, status);
1323         j = 32 + n;
1324 
1325         fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */
1326         fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status),
1327                            status); /* FP0 IS R = (X-Y1)-Y2 */
1328 
1329     tancont:
1330         if (n & 1) {
1331             /* NODD */
1332             fp1 = fp0; /* R */
1333             fp0 = floatx80_mul(fp0, fp0, status); /* S = R*R */
1334             fp3 = float64_to_floatx80(make_float64(0x3EA0B759F50F8688),
1335                                       status); /* Q4 */
1336             fp2 = float64_to_floatx80(make_float64(0xBEF2BAA5A8924F04),
1337                                       status); /* P3 */
1338             fp3 = floatx80_mul(fp3, fp0, status); /* SQ4 */
1339             fp2 = floatx80_mul(fp2, fp0, status); /* SP3 */
1340             fp3 = floatx80_add(fp3, float64_to_floatx80(
1341                                make_float64(0xBF346F59B39BA65F), status),
1342                                status); /* Q3+SQ4 */
1343             fp4 = packFloatx80(0, 0x3FF6, UINT64_C(0xE073D3FC199C4A00));
1344             fp2 = floatx80_add(fp2, fp4, status); /* P2+SP3 */
1345             fp3 = floatx80_mul(fp3, fp0, status); /* S(Q3+SQ4) */
1346             fp2 = floatx80_mul(fp2, fp0, status); /* S(P2+SP3) */
1347             fp4 = packFloatx80(0, 0x3FF9, UINT64_C(0xD23CD68415D95FA1));
1348             fp3 = floatx80_add(fp3, fp4, status); /* Q2+S(Q3+SQ4) */
1349             fp4 = packFloatx80(1, 0x3FFC, UINT64_C(0x8895A6C5FB423BCA));
1350             fp2 = floatx80_add(fp2, fp4, status); /* P1+S(P2+SP3) */
1351             fp3 = floatx80_mul(fp3, fp0, status); /* S(Q2+S(Q3+SQ4)) */
1352             fp2 = floatx80_mul(fp2, fp0, status); /* S(P1+S(P2+SP3)) */
1353             fp4 = packFloatx80(1, 0x3FFD, UINT64_C(0xEEF57E0DA84BC8CE));
1354             fp3 = floatx80_add(fp3, fp4, status); /* Q1+S(Q2+S(Q3+SQ4)) */
1355             fp2 = floatx80_mul(fp2, fp1, status); /* RS(P1+S(P2+SP3)) */
1356             fp0 = floatx80_mul(fp0, fp3, status); /* S(Q1+S(Q2+S(Q3+SQ4))) */
1357             fp1 = floatx80_add(fp1, fp2, status); /* R+RS(P1+S(P2+SP3)) */
1358             fp0 = floatx80_add(fp0, float32_to_floatx80(
1359                                make_float32(0x3F800000), status),
1360                                status); /* 1+S(Q1+S(Q2+S(Q3+SQ4))) */
1361 
1362             xSign = extractFloatx80Sign(fp1);
1363             xExp = extractFloatx80Exp(fp1);
1364             xSig = extractFloatx80Frac(fp1);
1365             xSign ^= 1;
1366             fp1 = packFloatx80(xSign, xExp, xSig);
1367 
1368             status->float_rounding_mode = user_rnd_mode;
1369             status->floatx80_rounding_precision = user_rnd_prec;
1370 
1371             a = floatx80_div(fp0, fp1, status);
1372 
1373             float_raise(float_flag_inexact, status);
1374 
1375             return a;
1376         } else {
1377             fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
1378             fp3 = float64_to_floatx80(make_float64(0x3EA0B759F50F8688),
1379                                       status); /* Q4 */
1380             fp2 = float64_to_floatx80(make_float64(0xBEF2BAA5A8924F04),
1381                                       status); /* P3 */
1382             fp3 = floatx80_mul(fp3, fp1, status); /* SQ4 */
1383             fp2 = floatx80_mul(fp2, fp1, status); /* SP3 */
1384             fp3 = floatx80_add(fp3, float64_to_floatx80(
1385                                make_float64(0xBF346F59B39BA65F), status),
1386                                status); /* Q3+SQ4 */
1387             fp4 = packFloatx80(0, 0x3FF6, UINT64_C(0xE073D3FC199C4A00));
1388             fp2 = floatx80_add(fp2, fp4, status); /* P2+SP3 */
1389             fp3 = floatx80_mul(fp3, fp1, status); /* S(Q3+SQ4) */
1390             fp2 = floatx80_mul(fp2, fp1, status); /* S(P2+SP3) */
1391             fp4 = packFloatx80(0, 0x3FF9, UINT64_C(0xD23CD68415D95FA1));
1392             fp3 = floatx80_add(fp3, fp4, status); /* Q2+S(Q3+SQ4) */
1393             fp4 = packFloatx80(1, 0x3FFC, UINT64_C(0x8895A6C5FB423BCA));
1394             fp2 = floatx80_add(fp2, fp4, status); /* P1+S(P2+SP3) */
1395             fp3 = floatx80_mul(fp3, fp1, status); /* S(Q2+S(Q3+SQ4)) */
1396             fp2 = floatx80_mul(fp2, fp1, status); /* S(P1+S(P2+SP3)) */
1397             fp4 = packFloatx80(1, 0x3FFD, UINT64_C(0xEEF57E0DA84BC8CE));
1398             fp3 = floatx80_add(fp3, fp4, status); /* Q1+S(Q2+S(Q3+SQ4)) */
1399             fp2 = floatx80_mul(fp2, fp0, status); /* RS(P1+S(P2+SP3)) */
1400             fp1 = floatx80_mul(fp1, fp3, status); /* S(Q1+S(Q2+S(Q3+SQ4))) */
1401             fp0 = floatx80_add(fp0, fp2, status); /* R+RS(P1+S(P2+SP3)) */
1402             fp1 = floatx80_add(fp1, float32_to_floatx80(
1403                                make_float32(0x3F800000), status),
1404                                status); /* 1+S(Q1+S(Q2+S(Q3+SQ4))) */
1405 
1406             status->float_rounding_mode = user_rnd_mode;
1407             status->floatx80_rounding_precision = user_rnd_prec;
1408 
1409             a = floatx80_div(fp0, fp1, status);
1410 
1411             float_raise(float_flag_inexact, status);
1412 
1413             return a;
1414         }
1415     }
1416 }
1417 
1418 /*
1419  * Sine
1420  */
1421 
1422 floatx80 floatx80_sin(floatx80 a, float_status *status)
1423 {
1424     bool aSign, xSign;
1425     int32_t aExp, xExp;
1426     uint64_t aSig, xSig;
1427 
1428     FloatRoundMode user_rnd_mode;
1429     FloatX80RoundPrec user_rnd_prec;
1430 
1431     int32_t compact, l, n, j;
1432     floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2;
1433     float32 posneg1, twoto63;
1434     bool endflag;
1435 
1436     aSig = extractFloatx80Frac(a);
1437     aExp = extractFloatx80Exp(a);
1438     aSign = extractFloatx80Sign(a);
1439 
1440     if (aExp == 0x7FFF) {
1441         if ((uint64_t) (aSig << 1)) {
1442             return propagateFloatx80NaNOneArg(a, status);
1443         }
1444         float_raise(float_flag_invalid, status);
1445         return floatx80_default_nan(status);
1446     }
1447 
1448     if (aExp == 0 && aSig == 0) {
1449         return packFloatx80(aSign, 0, 0);
1450     }
1451 
1452     user_rnd_mode = status->float_rounding_mode;
1453     user_rnd_prec = status->floatx80_rounding_precision;
1454     status->float_rounding_mode = float_round_nearest_even;
1455     status->floatx80_rounding_precision = floatx80_precision_x;
1456 
1457     compact = floatx80_make_compact(aExp, aSig);
1458 
1459     fp0 = a;
1460 
1461     if (compact < 0x3FD78000 || compact > 0x4004BC7E) {
1462         /* 2^(-40) > |X| > 15 PI */
1463         if (compact > 0x3FFF8000) { /* |X| >= 15 PI */
1464             /* REDUCEX */
1465             fp1 = packFloatx80(0, 0, 0);
1466             if (compact == 0x7FFEFFFF) {
1467                 twopi1 = packFloatx80(aSign ^ 1, 0x7FFE,
1468                                       UINT64_C(0xC90FDAA200000000));
1469                 twopi2 = packFloatx80(aSign ^ 1, 0x7FDC,
1470                                       UINT64_C(0x85A308D300000000));
1471                 fp0 = floatx80_add(fp0, twopi1, status);
1472                 fp1 = fp0;
1473                 fp0 = floatx80_add(fp0, twopi2, status);
1474                 fp1 = floatx80_sub(fp1, fp0, status);
1475                 fp1 = floatx80_add(fp1, twopi2, status);
1476             }
1477         loop:
1478             xSign = extractFloatx80Sign(fp0);
1479             xExp = extractFloatx80Exp(fp0);
1480             xExp -= 0x3FFF;
1481             if (xExp <= 28) {
1482                 l = 0;
1483                 endflag = true;
1484             } else {
1485                 l = xExp - 27;
1486                 endflag = false;
1487             }
1488             invtwopi = packFloatx80(0, 0x3FFE - l,
1489                                     UINT64_C(0xA2F9836E4E44152A)); /* INVTWOPI */
1490             twopi1 = packFloatx80(0, 0x3FFF + l, UINT64_C(0xC90FDAA200000000));
1491             twopi2 = packFloatx80(0, 0x3FDD + l, UINT64_C(0x85A308D300000000));
1492 
1493             /* SIGN(INARG)*2^63 IN SGL */
1494             twoto63 = packFloat32(xSign, 0xBE, 0);
1495 
1496             fp2 = floatx80_mul(fp0, invtwopi, status);
1497             fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status),
1498                                status); /* THE FRACT PART OF FP2 IS ROUNDED */
1499             fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status),
1500                                status); /* FP2 is N */
1501             fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */
1502             fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */
1503             fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */
1504             fp4 = floatx80_sub(fp4, fp3, status); /* W-P */
1505             fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */
1506             fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */
1507             fp3 = fp0; /* FP3 is A */
1508             fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */
1509             fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */
1510 
1511             if (endflag) {
1512                 n = floatx80_to_int32(fp2, status);
1513                 goto sincont;
1514             }
1515             fp3 = floatx80_sub(fp3, fp0, status); /* A-R */
1516             fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */
1517             goto loop;
1518         } else {
1519             /* SINSM */
1520             fp0 = float32_to_floatx80(make_float32(0x3F800000),
1521                                       status); /* 1 */
1522 
1523             status->float_rounding_mode = user_rnd_mode;
1524             status->floatx80_rounding_precision = user_rnd_prec;
1525 
1526             /* SINTINY */
1527             a = floatx80_move(a, status);
1528             float_raise(float_flag_inexact, status);
1529 
1530             return a;
1531         }
1532     } else {
1533         fp1 = floatx80_mul(fp0, float64_to_floatx80(
1534                            make_float64(0x3FE45F306DC9C883), status),
1535                            status); /* X*2/PI */
1536 
1537         n = floatx80_to_int32(fp1, status);
1538         j = 32 + n;
1539 
1540         fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */
1541         fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status),
1542                            status); /* FP0 IS R = (X-Y1)-Y2 */
1543 
1544     sincont:
1545         if (n & 1) {
1546             /* COSPOLY */
1547             fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
1548             fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
1549             fp2 = float64_to_floatx80(make_float64(0x3D2AC4D0D6011EE3),
1550                                       status); /* B8 */
1551             fp3 = float64_to_floatx80(make_float64(0xBDA9396F9F45AC19),
1552                                       status); /* B7 */
1553 
1554             xSign = extractFloatx80Sign(fp0); /* X IS S */
1555             xExp = extractFloatx80Exp(fp0);
1556             xSig = extractFloatx80Frac(fp0);
1557 
1558             if ((n >> 1) & 1) {
1559                 xSign ^= 1;
1560                 posneg1 = make_float32(0xBF800000); /* -1 */
1561             } else {
1562                 xSign ^= 0;
1563                 posneg1 = make_float32(0x3F800000); /* 1 */
1564             } /* X IS NOW R'= SGN*R */
1565 
1566             fp2 = floatx80_mul(fp2, fp1, status); /* TB8 */
1567             fp3 = floatx80_mul(fp3, fp1, status); /* TB7 */
1568             fp2 = floatx80_add(fp2, float64_to_floatx80(
1569                                make_float64(0x3E21EED90612C972), status),
1570                                status); /* B6+TB8 */
1571             fp3 = floatx80_add(fp3, float64_to_floatx80(
1572                                make_float64(0xBE927E4FB79D9FCF), status),
1573                                status); /* B5+TB7 */
1574             fp2 = floatx80_mul(fp2, fp1, status); /* T(B6+TB8) */
1575             fp3 = floatx80_mul(fp3, fp1, status); /* T(B5+TB7) */
1576             fp2 = floatx80_add(fp2, float64_to_floatx80(
1577                                make_float64(0x3EFA01A01A01D423), status),
1578                                status); /* B4+T(B6+TB8) */
1579             fp4 = packFloatx80(1, 0x3FF5, UINT64_C(0xB60B60B60B61D438));
1580             fp3 = floatx80_add(fp3, fp4, status); /* B3+T(B5+TB7) */
1581             fp2 = floatx80_mul(fp2, fp1, status); /* T(B4+T(B6+TB8)) */
1582             fp1 = floatx80_mul(fp1, fp3, status); /* T(B3+T(B5+TB7)) */
1583             fp4 = packFloatx80(0, 0x3FFA, UINT64_C(0xAAAAAAAAAAAAAB5E));
1584             fp2 = floatx80_add(fp2, fp4, status); /* B2+T(B4+T(B6+TB8)) */
1585             fp1 = floatx80_add(fp1, float32_to_floatx80(
1586                                make_float32(0xBF000000), status),
1587                                status); /* B1+T(B3+T(B5+TB7)) */
1588             fp0 = floatx80_mul(fp0, fp2, status); /* S(B2+T(B4+T(B6+TB8))) */
1589             fp0 = floatx80_add(fp0, fp1, status); /* [B1+T(B3+T(B5+TB7))]+
1590                                                    * [S(B2+T(B4+T(B6+TB8)))]
1591                                                    */
1592 
1593             x = packFloatx80(xSign, xExp, xSig);
1594             fp0 = floatx80_mul(fp0, x, status);
1595 
1596             status->float_rounding_mode = user_rnd_mode;
1597             status->floatx80_rounding_precision = user_rnd_prec;
1598 
1599             a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status);
1600 
1601             float_raise(float_flag_inexact, status);
1602 
1603             return a;
1604         } else {
1605             /* SINPOLY */
1606             xSign = extractFloatx80Sign(fp0); /* X IS R */
1607             xExp = extractFloatx80Exp(fp0);
1608             xSig = extractFloatx80Frac(fp0);
1609 
1610             xSign ^= (n >> 1) & 1; /* X IS NOW R'= SGN*R */
1611 
1612             fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
1613             fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
1614             fp3 = float64_to_floatx80(make_float64(0xBD6AAA77CCC994F5),
1615                                       status); /* A7 */
1616             fp2 = float64_to_floatx80(make_float64(0x3DE612097AAE8DA1),
1617                                       status); /* A6 */
1618             fp3 = floatx80_mul(fp3, fp1, status); /* T*A7 */
1619             fp2 = floatx80_mul(fp2, fp1, status); /* T*A6 */
1620             fp3 = floatx80_add(fp3, float64_to_floatx80(
1621                                make_float64(0xBE5AE6452A118AE4), status),
1622                                status); /* A5+T*A7 */
1623             fp2 = floatx80_add(fp2, float64_to_floatx80(
1624                                make_float64(0x3EC71DE3A5341531), status),
1625                                status); /* A4+T*A6 */
1626             fp3 = floatx80_mul(fp3, fp1, status); /* T(A5+TA7) */
1627             fp2 = floatx80_mul(fp2, fp1, status); /* T(A4+TA6) */
1628             fp3 = floatx80_add(fp3, float64_to_floatx80(
1629                                make_float64(0xBF2A01A01A018B59), status),
1630                                status); /* A3+T(A5+TA7) */
1631             fp4 = packFloatx80(0, 0x3FF8, UINT64_C(0x88888888888859AF));
1632             fp2 = floatx80_add(fp2, fp4, status); /* A2+T(A4+TA6) */
1633             fp1 = floatx80_mul(fp1, fp3, status); /* T(A3+T(A5+TA7)) */
1634             fp2 = floatx80_mul(fp2, fp0, status); /* S(A2+T(A4+TA6)) */
1635             fp4 = packFloatx80(1, 0x3FFC, UINT64_C(0xAAAAAAAAAAAAAA99));
1636             fp1 = floatx80_add(fp1, fp4, status); /* A1+T(A3+T(A5+TA7)) */
1637             fp1 = floatx80_add(fp1, fp2,
1638                                status); /* [A1+T(A3+T(A5+TA7))]+
1639                                          * [S(A2+T(A4+TA6))]
1640                                          */
1641 
1642             x = packFloatx80(xSign, xExp, xSig);
1643             fp0 = floatx80_mul(fp0, x, status); /* R'*S */
1644             fp0 = floatx80_mul(fp0, fp1, status); /* SIN(R')-R' */
1645 
1646             status->float_rounding_mode = user_rnd_mode;
1647             status->floatx80_rounding_precision = user_rnd_prec;
1648 
1649             a = floatx80_add(fp0, x, status);
1650 
1651             float_raise(float_flag_inexact, status);
1652 
1653             return a;
1654         }
1655     }
1656 }
1657 
1658 /*
1659  * Cosine
1660  */
1661 
1662 floatx80 floatx80_cos(floatx80 a, float_status *status)
1663 {
1664     bool aSign, xSign;
1665     int32_t aExp, xExp;
1666     uint64_t aSig, xSig;
1667 
1668     FloatRoundMode user_rnd_mode;
1669     FloatX80RoundPrec user_rnd_prec;
1670 
1671     int32_t compact, l, n, j;
1672     floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2;
1673     float32 posneg1, twoto63;
1674     bool endflag;
1675 
1676     aSig = extractFloatx80Frac(a);
1677     aExp = extractFloatx80Exp(a);
1678     aSign = extractFloatx80Sign(a);
1679 
1680     if (aExp == 0x7FFF) {
1681         if ((uint64_t) (aSig << 1)) {
1682             return propagateFloatx80NaNOneArg(a, status);
1683         }
1684         float_raise(float_flag_invalid, status);
1685         return floatx80_default_nan(status);
1686     }
1687 
1688     if (aExp == 0 && aSig == 0) {
1689         return packFloatx80(0, one_exp, one_sig);
1690     }
1691 
1692     user_rnd_mode = status->float_rounding_mode;
1693     user_rnd_prec = status->floatx80_rounding_precision;
1694     status->float_rounding_mode = float_round_nearest_even;
1695     status->floatx80_rounding_precision = floatx80_precision_x;
1696 
1697     compact = floatx80_make_compact(aExp, aSig);
1698 
1699     fp0 = a;
1700 
1701     if (compact < 0x3FD78000 || compact > 0x4004BC7E) {
1702         /* 2^(-40) > |X| > 15 PI */
1703         if (compact > 0x3FFF8000) { /* |X| >= 15 PI */
1704             /* REDUCEX */
1705             fp1 = packFloatx80(0, 0, 0);
1706             if (compact == 0x7FFEFFFF) {
1707                 twopi1 = packFloatx80(aSign ^ 1, 0x7FFE,
1708                                       UINT64_C(0xC90FDAA200000000));
1709                 twopi2 = packFloatx80(aSign ^ 1, 0x7FDC,
1710                                       UINT64_C(0x85A308D300000000));
1711                 fp0 = floatx80_add(fp0, twopi1, status);
1712                 fp1 = fp0;
1713                 fp0 = floatx80_add(fp0, twopi2, status);
1714                 fp1 = floatx80_sub(fp1, fp0, status);
1715                 fp1 = floatx80_add(fp1, twopi2, status);
1716             }
1717         loop:
1718             xSign = extractFloatx80Sign(fp0);
1719             xExp = extractFloatx80Exp(fp0);
1720             xExp -= 0x3FFF;
1721             if (xExp <= 28) {
1722                 l = 0;
1723                 endflag = true;
1724             } else {
1725                 l = xExp - 27;
1726                 endflag = false;
1727             }
1728             invtwopi = packFloatx80(0, 0x3FFE - l,
1729                                     UINT64_C(0xA2F9836E4E44152A)); /* INVTWOPI */
1730             twopi1 = packFloatx80(0, 0x3FFF + l, UINT64_C(0xC90FDAA200000000));
1731             twopi2 = packFloatx80(0, 0x3FDD + l, UINT64_C(0x85A308D300000000));
1732 
1733             /* SIGN(INARG)*2^63 IN SGL */
1734             twoto63 = packFloat32(xSign, 0xBE, 0);
1735 
1736             fp2 = floatx80_mul(fp0, invtwopi, status);
1737             fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status),
1738                                status); /* THE FRACT PART OF FP2 IS ROUNDED */
1739             fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status),
1740                                status); /* FP2 is N */
1741             fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */
1742             fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */
1743             fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */
1744             fp4 = floatx80_sub(fp4, fp3, status); /* W-P */
1745             fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */
1746             fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */
1747             fp3 = fp0; /* FP3 is A */
1748             fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */
1749             fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */
1750 
1751             if (endflag) {
1752                 n = floatx80_to_int32(fp2, status);
1753                 goto sincont;
1754             }
1755             fp3 = floatx80_sub(fp3, fp0, status); /* A-R */
1756             fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */
1757             goto loop;
1758         } else {
1759             /* SINSM */
1760             fp0 = float32_to_floatx80(make_float32(0x3F800000), status); /* 1 */
1761 
1762             status->float_rounding_mode = user_rnd_mode;
1763             status->floatx80_rounding_precision = user_rnd_prec;
1764 
1765             /* COSTINY */
1766             a = floatx80_sub(fp0, float32_to_floatx80(
1767                              make_float32(0x00800000), status),
1768                              status);
1769             float_raise(float_flag_inexact, status);
1770 
1771             return a;
1772         }
1773     } else {
1774         fp1 = floatx80_mul(fp0, float64_to_floatx80(
1775                            make_float64(0x3FE45F306DC9C883), status),
1776                            status); /* X*2/PI */
1777 
1778         n = floatx80_to_int32(fp1, status);
1779         j = 32 + n;
1780 
1781         fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */
1782         fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status),
1783                            status); /* FP0 IS R = (X-Y1)-Y2 */
1784 
1785     sincont:
1786         if ((n + 1) & 1) {
1787             /* COSPOLY */
1788             fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
1789             fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
1790             fp2 = float64_to_floatx80(make_float64(0x3D2AC4D0D6011EE3),
1791                                       status); /* B8 */
1792             fp3 = float64_to_floatx80(make_float64(0xBDA9396F9F45AC19),
1793                                       status); /* B7 */
1794 
1795             xSign = extractFloatx80Sign(fp0); /* X IS S */
1796             xExp = extractFloatx80Exp(fp0);
1797             xSig = extractFloatx80Frac(fp0);
1798 
1799             if (((n + 1) >> 1) & 1) {
1800                 xSign ^= 1;
1801                 posneg1 = make_float32(0xBF800000); /* -1 */
1802             } else {
1803                 xSign ^= 0;
1804                 posneg1 = make_float32(0x3F800000); /* 1 */
1805             } /* X IS NOW R'= SGN*R */
1806 
1807             fp2 = floatx80_mul(fp2, fp1, status); /* TB8 */
1808             fp3 = floatx80_mul(fp3, fp1, status); /* TB7 */
1809             fp2 = floatx80_add(fp2, float64_to_floatx80(
1810                                make_float64(0x3E21EED90612C972), status),
1811                                status); /* B6+TB8 */
1812             fp3 = floatx80_add(fp3, float64_to_floatx80(
1813                                make_float64(0xBE927E4FB79D9FCF), status),
1814                                status); /* B5+TB7 */
1815             fp2 = floatx80_mul(fp2, fp1, status); /* T(B6+TB8) */
1816             fp3 = floatx80_mul(fp3, fp1, status); /* T(B5+TB7) */
1817             fp2 = floatx80_add(fp2, float64_to_floatx80(
1818                                make_float64(0x3EFA01A01A01D423), status),
1819                                status); /* B4+T(B6+TB8) */
1820             fp4 = packFloatx80(1, 0x3FF5, UINT64_C(0xB60B60B60B61D438));
1821             fp3 = floatx80_add(fp3, fp4, status); /* B3+T(B5+TB7) */
1822             fp2 = floatx80_mul(fp2, fp1, status); /* T(B4+T(B6+TB8)) */
1823             fp1 = floatx80_mul(fp1, fp3, status); /* T(B3+T(B5+TB7)) */
1824             fp4 = packFloatx80(0, 0x3FFA, UINT64_C(0xAAAAAAAAAAAAAB5E));
1825             fp2 = floatx80_add(fp2, fp4, status); /* B2+T(B4+T(B6+TB8)) */
1826             fp1 = floatx80_add(fp1, float32_to_floatx80(
1827                                make_float32(0xBF000000), status),
1828                                status); /* B1+T(B3+T(B5+TB7)) */
1829             fp0 = floatx80_mul(fp0, fp2, status); /* S(B2+T(B4+T(B6+TB8))) */
1830             fp0 = floatx80_add(fp0, fp1, status);
1831                               /* [B1+T(B3+T(B5+TB7))]+[S(B2+T(B4+T(B6+TB8)))] */
1832 
1833             x = packFloatx80(xSign, xExp, xSig);
1834             fp0 = floatx80_mul(fp0, x, status);
1835 
1836             status->float_rounding_mode = user_rnd_mode;
1837             status->floatx80_rounding_precision = user_rnd_prec;
1838 
1839             a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status);
1840 
1841             float_raise(float_flag_inexact, status);
1842 
1843             return a;
1844         } else {
1845             /* SINPOLY */
1846             xSign = extractFloatx80Sign(fp0); /* X IS R */
1847             xExp = extractFloatx80Exp(fp0);
1848             xSig = extractFloatx80Frac(fp0);
1849 
1850             xSign ^= ((n + 1) >> 1) & 1; /* X IS NOW R'= SGN*R */
1851 
1852             fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
1853             fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
1854             fp3 = float64_to_floatx80(make_float64(0xBD6AAA77CCC994F5),
1855                                       status); /* A7 */
1856             fp2 = float64_to_floatx80(make_float64(0x3DE612097AAE8DA1),
1857                                       status); /* A6 */
1858             fp3 = floatx80_mul(fp3, fp1, status); /* T*A7 */
1859             fp2 = floatx80_mul(fp2, fp1, status); /* T*A6 */
1860             fp3 = floatx80_add(fp3, float64_to_floatx80(
1861                                make_float64(0xBE5AE6452A118AE4), status),
1862                                status); /* A5+T*A7 */
1863             fp2 = floatx80_add(fp2, float64_to_floatx80(
1864                                make_float64(0x3EC71DE3A5341531), status),
1865                                status); /* A4+T*A6 */
1866             fp3 = floatx80_mul(fp3, fp1, status); /* T(A5+TA7) */
1867             fp2 = floatx80_mul(fp2, fp1, status); /* T(A4+TA6) */
1868             fp3 = floatx80_add(fp3, float64_to_floatx80(
1869                                make_float64(0xBF2A01A01A018B59), status),
1870                                status); /* A3+T(A5+TA7) */
1871             fp4 = packFloatx80(0, 0x3FF8, UINT64_C(0x88888888888859AF));
1872             fp2 = floatx80_add(fp2, fp4, status); /* A2+T(A4+TA6) */
1873             fp1 = floatx80_mul(fp1, fp3, status); /* T(A3+T(A5+TA7)) */
1874             fp2 = floatx80_mul(fp2, fp0, status); /* S(A2+T(A4+TA6)) */
1875             fp4 = packFloatx80(1, 0x3FFC, UINT64_C(0xAAAAAAAAAAAAAA99));
1876             fp1 = floatx80_add(fp1, fp4, status); /* A1+T(A3+T(A5+TA7)) */
1877             fp1 = floatx80_add(fp1, fp2, status);
1878                                     /* [A1+T(A3+T(A5+TA7))]+[S(A2+T(A4+TA6))] */
1879 
1880             x = packFloatx80(xSign, xExp, xSig);
1881             fp0 = floatx80_mul(fp0, x, status); /* R'*S */
1882             fp0 = floatx80_mul(fp0, fp1, status); /* SIN(R')-R' */
1883 
1884             status->float_rounding_mode = user_rnd_mode;
1885             status->floatx80_rounding_precision = user_rnd_prec;
1886 
1887             a = floatx80_add(fp0, x, status);
1888 
1889             float_raise(float_flag_inexact, status);
1890 
1891             return a;
1892         }
1893     }
1894 }
1895 
1896 /*
1897  * Arc tangent
1898  */
1899 
1900 floatx80 floatx80_atan(floatx80 a, float_status *status)
1901 {
1902     bool aSign;
1903     int32_t aExp;
1904     uint64_t aSig;
1905 
1906     FloatRoundMode user_rnd_mode;
1907     FloatX80RoundPrec user_rnd_prec;
1908 
1909     int32_t compact, tbl_index;
1910     floatx80 fp0, fp1, fp2, fp3, xsave;
1911 
1912     aSig = extractFloatx80Frac(a);
1913     aExp = extractFloatx80Exp(a);
1914     aSign = extractFloatx80Sign(a);
1915 
1916     if (aExp == 0x7FFF) {
1917         if ((uint64_t) (aSig << 1)) {
1918             return propagateFloatx80NaNOneArg(a, status);
1919         }
1920         a = packFloatx80(aSign, piby2_exp, pi_sig);
1921         float_raise(float_flag_inexact, status);
1922         return floatx80_move(a, status);
1923     }
1924 
1925     if (aExp == 0 && aSig == 0) {
1926         return packFloatx80(aSign, 0, 0);
1927     }
1928 
1929     compact = floatx80_make_compact(aExp, aSig);
1930 
1931     user_rnd_mode = status->float_rounding_mode;
1932     user_rnd_prec = status->floatx80_rounding_precision;
1933     status->float_rounding_mode = float_round_nearest_even;
1934     status->floatx80_rounding_precision = floatx80_precision_x;
1935 
1936     if (compact < 0x3FFB8000 || compact > 0x4002FFFF) {
1937         /* |X| >= 16 or |X| < 1/16 */
1938         if (compact > 0x3FFF8000) { /* |X| >= 16 */
1939             if (compact > 0x40638000) { /* |X| > 2^(100) */
1940                 fp0 = packFloatx80(aSign, piby2_exp, pi_sig);
1941                 fp1 = packFloatx80(aSign, 0x0001, one_sig);
1942 
1943                 status->float_rounding_mode = user_rnd_mode;
1944                 status->floatx80_rounding_precision = user_rnd_prec;
1945 
1946                 a = floatx80_sub(fp0, fp1, status);
1947 
1948                 float_raise(float_flag_inexact, status);
1949 
1950                 return a;
1951             } else {
1952                 fp0 = a;
1953                 fp1 = packFloatx80(1, one_exp, one_sig); /* -1 */
1954                 fp1 = floatx80_div(fp1, fp0, status); /* X' = -1/X */
1955                 xsave = fp1;
1956                 fp0 = floatx80_mul(fp1, fp1, status); /* Y = X'*X' */
1957                 fp1 = floatx80_mul(fp0, fp0, status); /* Z = Y*Y */
1958                 fp3 = float64_to_floatx80(make_float64(0xBFB70BF398539E6A),
1959                                           status); /* C5 */
1960                 fp2 = float64_to_floatx80(make_float64(0x3FBC7187962D1D7D),
1961                                           status); /* C4 */
1962                 fp3 = floatx80_mul(fp3, fp1, status); /* Z*C5 */
1963                 fp2 = floatx80_mul(fp2, fp1, status); /* Z*C4 */
1964                 fp3 = floatx80_add(fp3, float64_to_floatx80(
1965                                    make_float64(0xBFC24924827107B8), status),
1966                                    status); /* C3+Z*C5 */
1967                 fp2 = floatx80_add(fp2, float64_to_floatx80(
1968                                    make_float64(0x3FC999999996263E), status),
1969                                    status); /* C2+Z*C4 */
1970                 fp1 = floatx80_mul(fp1, fp3, status); /* Z*(C3+Z*C5) */
1971                 fp2 = floatx80_mul(fp2, fp0, status); /* Y*(C2+Z*C4) */
1972                 fp1 = floatx80_add(fp1, float64_to_floatx80(
1973                                    make_float64(0xBFD5555555555536), status),
1974                                    status); /* C1+Z*(C3+Z*C5) */
1975                 fp0 = floatx80_mul(fp0, xsave, status); /* X'*Y */
1976                 /* [Y*(C2+Z*C4)]+[C1+Z*(C3+Z*C5)] */
1977                 fp1 = floatx80_add(fp1, fp2, status);
1978                 /* X'*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) ?? */
1979                 fp0 = floatx80_mul(fp0, fp1, status);
1980                 fp0 = floatx80_add(fp0, xsave, status);
1981                 fp1 = packFloatx80(aSign, piby2_exp, pi_sig);
1982 
1983                 status->float_rounding_mode = user_rnd_mode;
1984                 status->floatx80_rounding_precision = user_rnd_prec;
1985 
1986                 a = floatx80_add(fp0, fp1, status);
1987 
1988                 float_raise(float_flag_inexact, status);
1989 
1990                 return a;
1991             }
1992         } else { /* |X| < 1/16 */
1993             if (compact < 0x3FD78000) { /* |X| < 2^(-40) */
1994                 status->float_rounding_mode = user_rnd_mode;
1995                 status->floatx80_rounding_precision = user_rnd_prec;
1996 
1997                 a = floatx80_move(a, status);
1998 
1999                 float_raise(float_flag_inexact, status);
2000 
2001                 return a;
2002             } else {
2003                 fp0 = a;
2004                 xsave = a;
2005                 fp0 = floatx80_mul(fp0, fp0, status); /* Y = X*X */
2006                 fp1 = floatx80_mul(fp0, fp0, status); /* Z = Y*Y */
2007                 fp2 = float64_to_floatx80(make_float64(0x3FB344447F876989),
2008                                           status); /* B6 */
2009                 fp3 = float64_to_floatx80(make_float64(0xBFB744EE7FAF45DB),
2010                                           status); /* B5 */
2011                 fp2 = floatx80_mul(fp2, fp1, status); /* Z*B6 */
2012                 fp3 = floatx80_mul(fp3, fp1, status); /* Z*B5 */
2013                 fp2 = floatx80_add(fp2, float64_to_floatx80(
2014                                    make_float64(0x3FBC71C646940220), status),
2015                                    status); /* B4+Z*B6 */
2016                 fp3 = floatx80_add(fp3, float64_to_floatx80(
2017                                    make_float64(0xBFC24924921872F9),
2018                                    status), status); /* B3+Z*B5 */
2019                 fp2 = floatx80_mul(fp2, fp1, status); /* Z*(B4+Z*B6) */
2020                 fp1 = floatx80_mul(fp1, fp3, status); /* Z*(B3+Z*B5) */
2021                 fp2 = floatx80_add(fp2, float64_to_floatx80(
2022                                    make_float64(0x3FC9999999998FA9), status),
2023                                    status); /* B2+Z*(B4+Z*B6) */
2024                 fp1 = floatx80_add(fp1, float64_to_floatx80(
2025                                    make_float64(0xBFD5555555555555), status),
2026                                    status); /* B1+Z*(B3+Z*B5) */
2027                 fp2 = floatx80_mul(fp2, fp0, status); /* Y*(B2+Z*(B4+Z*B6)) */
2028                 fp0 = floatx80_mul(fp0, xsave, status); /* X*Y */
2029                 /* [B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))] */
2030                 fp1 = floatx80_add(fp1, fp2, status);
2031                 /* X*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) */
2032                 fp0 = floatx80_mul(fp0, fp1, status);
2033 
2034                 status->float_rounding_mode = user_rnd_mode;
2035                 status->floatx80_rounding_precision = user_rnd_prec;
2036 
2037                 a = floatx80_add(fp0, xsave, status);
2038 
2039                 float_raise(float_flag_inexact, status);
2040 
2041                 return a;
2042             }
2043         }
2044     } else {
2045         aSig &= UINT64_C(0xF800000000000000);
2046         aSig |= UINT64_C(0x0400000000000000);
2047         xsave = packFloatx80(aSign, aExp, aSig); /* F */
2048         fp0 = a;
2049         fp1 = a; /* X */
2050         fp2 = packFloatx80(0, one_exp, one_sig); /* 1 */
2051         fp1 = floatx80_mul(fp1, xsave, status); /* X*F */
2052         fp0 = floatx80_sub(fp0, xsave, status); /* X-F */
2053         fp1 = floatx80_add(fp1, fp2, status); /* 1 + X*F */
2054         fp0 = floatx80_div(fp0, fp1, status); /* U = (X-F)/(1+X*F) */
2055 
2056         tbl_index = compact;
2057 
2058         tbl_index &= 0x7FFF0000;
2059         tbl_index -= 0x3FFB0000;
2060         tbl_index >>= 1;
2061         tbl_index += compact & 0x00007800;
2062         tbl_index >>= 11;
2063 
2064         fp3 = atan_tbl[tbl_index];
2065 
2066         fp3.high |= aSign ? 0x8000 : 0; /* ATAN(F) */
2067 
2068         fp1 = floatx80_mul(fp0, fp0, status); /* V = U*U */
2069         fp2 = float64_to_floatx80(make_float64(0xBFF6687E314987D8),
2070                                   status); /* A3 */
2071         fp2 = floatx80_add(fp2, fp1, status); /* A3+V */
2072         fp2 = floatx80_mul(fp2, fp1, status); /* V*(A3+V) */
2073         fp1 = floatx80_mul(fp1, fp0, status); /* U*V */
2074         fp2 = floatx80_add(fp2, float64_to_floatx80(
2075                            make_float64(0x4002AC6934A26DB3), status),
2076                            status); /* A2+V*(A3+V) */
2077         fp1 = floatx80_mul(fp1, float64_to_floatx80(
2078                            make_float64(0xBFC2476F4E1DA28E), status),
2079                            status); /* A1+U*V */
2080         fp1 = floatx80_mul(fp1, fp2, status); /* A1*U*V*(A2+V*(A3+V)) */
2081         fp0 = floatx80_add(fp0, fp1, status); /* ATAN(U) */
2082 
2083         status->float_rounding_mode = user_rnd_mode;
2084         status->floatx80_rounding_precision = user_rnd_prec;
2085 
2086         a = floatx80_add(fp0, fp3, status); /* ATAN(X) */
2087 
2088         float_raise(float_flag_inexact, status);
2089 
2090         return a;
2091     }
2092 }
2093 
2094 /*
2095  * Arc sine
2096  */
2097 
2098 floatx80 floatx80_asin(floatx80 a, float_status *status)
2099 {
2100     bool aSign;
2101     int32_t aExp;
2102     uint64_t aSig;
2103 
2104     FloatRoundMode user_rnd_mode;
2105     FloatX80RoundPrec user_rnd_prec;
2106 
2107     int32_t compact;
2108     floatx80 fp0, fp1, fp2, one;
2109 
2110     aSig = extractFloatx80Frac(a);
2111     aExp = extractFloatx80Exp(a);
2112     aSign = extractFloatx80Sign(a);
2113 
2114     if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) {
2115         return propagateFloatx80NaNOneArg(a, status);
2116     }
2117 
2118     if (aExp == 0 && aSig == 0) {
2119         return packFloatx80(aSign, 0, 0);
2120     }
2121 
2122     compact = floatx80_make_compact(aExp, aSig);
2123 
2124     if (compact >= 0x3FFF8000) { /* |X| >= 1 */
2125         if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */
2126             float_raise(float_flag_inexact, status);
2127             a = packFloatx80(aSign, piby2_exp, pi_sig);
2128             return floatx80_move(a, status);
2129         } else { /* |X| > 1 */
2130             float_raise(float_flag_invalid, status);
2131             return floatx80_default_nan(status);
2132         }
2133 
2134     } /* |X| < 1 */
2135 
2136     user_rnd_mode = status->float_rounding_mode;
2137     user_rnd_prec = status->floatx80_rounding_precision;
2138     status->float_rounding_mode = float_round_nearest_even;
2139     status->floatx80_rounding_precision = floatx80_precision_x;
2140 
2141     one = packFloatx80(0, one_exp, one_sig);
2142     fp0 = a;
2143 
2144     fp1 = floatx80_sub(one, fp0, status);   /* 1 - X */
2145     fp2 = floatx80_add(one, fp0, status);   /* 1 + X */
2146     fp1 = floatx80_mul(fp2, fp1, status);   /* (1+X)*(1-X) */
2147     fp1 = floatx80_sqrt(fp1, status);       /* SQRT((1+X)*(1-X)) */
2148     fp0 = floatx80_div(fp0, fp1, status);   /* X/SQRT((1+X)*(1-X)) */
2149 
2150     status->float_rounding_mode = user_rnd_mode;
2151     status->floatx80_rounding_precision = user_rnd_prec;
2152 
2153     a = floatx80_atan(fp0, status);         /* ATAN(X/SQRT((1+X)*(1-X))) */
2154 
2155     float_raise(float_flag_inexact, status);
2156 
2157     return a;
2158 }
2159 
2160 /*
2161  * Arc cosine
2162  */
2163 
2164 floatx80 floatx80_acos(floatx80 a, float_status *status)
2165 {
2166     bool aSign;
2167     int32_t aExp;
2168     uint64_t aSig;
2169 
2170     FloatRoundMode user_rnd_mode;
2171     FloatX80RoundPrec user_rnd_prec;
2172 
2173     int32_t compact;
2174     floatx80 fp0, fp1, one;
2175 
2176     aSig = extractFloatx80Frac(a);
2177     aExp = extractFloatx80Exp(a);
2178     aSign = extractFloatx80Sign(a);
2179 
2180     if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) {
2181         return propagateFloatx80NaNOneArg(a, status);
2182     }
2183     if (aExp == 0 && aSig == 0) {
2184         float_raise(float_flag_inexact, status);
2185         return roundAndPackFloatx80(status->floatx80_rounding_precision, 0,
2186                                     piby2_exp, pi_sig, 0, status);
2187     }
2188 
2189     compact = floatx80_make_compact(aExp, aSig);
2190 
2191     if (compact >= 0x3FFF8000) { /* |X| >= 1 */
2192         if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */
2193             if (aSign) { /* X == -1 */
2194                 a = packFloatx80(0, pi_exp, pi_sig);
2195                 float_raise(float_flag_inexact, status);
2196                 return floatx80_move(a, status);
2197             } else { /* X == +1 */
2198                 return packFloatx80(0, 0, 0);
2199             }
2200         } else { /* |X| > 1 */
2201             float_raise(float_flag_invalid, status);
2202             return floatx80_default_nan(status);
2203         }
2204     } /* |X| < 1 */
2205 
2206     user_rnd_mode = status->float_rounding_mode;
2207     user_rnd_prec = status->floatx80_rounding_precision;
2208     status->float_rounding_mode = float_round_nearest_even;
2209     status->floatx80_rounding_precision = floatx80_precision_x;
2210 
2211     one = packFloatx80(0, one_exp, one_sig);
2212     fp0 = a;
2213 
2214     fp1 = floatx80_add(one, fp0, status);   /* 1 + X */
2215     fp0 = floatx80_sub(one, fp0, status);   /* 1 - X */
2216     fp0 = floatx80_div(fp0, fp1, status);   /* (1-X)/(1+X) */
2217     fp0 = floatx80_sqrt(fp0, status);       /* SQRT((1-X)/(1+X)) */
2218     fp0 = floatx80_atan(fp0, status);       /* ATAN(SQRT((1-X)/(1+X))) */
2219 
2220     status->float_rounding_mode = user_rnd_mode;
2221     status->floatx80_rounding_precision = user_rnd_prec;
2222 
2223     a = floatx80_add(fp0, fp0, status);     /* 2 * ATAN(SQRT((1-X)/(1+X))) */
2224 
2225     float_raise(float_flag_inexact, status);
2226 
2227     return a;
2228 }
2229 
2230 /*
2231  * Hyperbolic arc tangent
2232  */
2233 
2234 floatx80 floatx80_atanh(floatx80 a, float_status *status)
2235 {
2236     bool aSign;
2237     int32_t aExp;
2238     uint64_t aSig;
2239 
2240     FloatRoundMode user_rnd_mode;
2241     FloatX80RoundPrec user_rnd_prec;
2242 
2243     int32_t compact;
2244     floatx80 fp0, fp1, fp2, one;
2245 
2246     aSig = extractFloatx80Frac(a);
2247     aExp = extractFloatx80Exp(a);
2248     aSign = extractFloatx80Sign(a);
2249 
2250     if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) {
2251         return propagateFloatx80NaNOneArg(a, status);
2252     }
2253 
2254     if (aExp == 0 && aSig == 0) {
2255         return packFloatx80(aSign, 0, 0);
2256     }
2257 
2258     compact = floatx80_make_compact(aExp, aSig);
2259 
2260     if (compact >= 0x3FFF8000) { /* |X| >= 1 */
2261         if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */
2262             float_raise(float_flag_divbyzero, status);
2263             return packFloatx80(aSign, floatx80_infinity.high,
2264                                 floatx80_infinity.low);
2265         } else { /* |X| > 1 */
2266             float_raise(float_flag_invalid, status);
2267             return floatx80_default_nan(status);
2268         }
2269     } /* |X| < 1 */
2270 
2271     user_rnd_mode = status->float_rounding_mode;
2272     user_rnd_prec = status->floatx80_rounding_precision;
2273     status->float_rounding_mode = float_round_nearest_even;
2274     status->floatx80_rounding_precision = floatx80_precision_x;
2275 
2276     one = packFloatx80(0, one_exp, one_sig);
2277     fp2 = packFloatx80(aSign, 0x3FFE, one_sig); /* SIGN(X) * (1/2) */
2278     fp0 = packFloatx80(0, aExp, aSig); /* Y = |X| */
2279     fp1 = packFloatx80(1, aExp, aSig); /* -Y */
2280     fp0 = floatx80_add(fp0, fp0, status); /* 2Y */
2281     fp1 = floatx80_add(fp1, one, status); /* 1-Y */
2282     fp0 = floatx80_div(fp0, fp1, status); /* Z = 2Y/(1-Y) */
2283     fp0 = floatx80_lognp1(fp0, status); /* LOG1P(Z) */
2284 
2285     status->float_rounding_mode = user_rnd_mode;
2286     status->floatx80_rounding_precision = user_rnd_prec;
2287 
2288     a = floatx80_mul(fp0, fp2,
2289                      status); /* ATANH(X) = SIGN(X) * (1/2) * LOG1P(Z) */
2290 
2291     float_raise(float_flag_inexact, status);
2292 
2293     return a;
2294 }
2295 
2296 /*
2297  * e to x minus 1
2298  */
2299 
2300 floatx80 floatx80_etoxm1(floatx80 a, float_status *status)
2301 {
2302     bool aSign;
2303     int32_t aExp;
2304     uint64_t aSig;
2305 
2306     FloatRoundMode user_rnd_mode;
2307     FloatX80RoundPrec user_rnd_prec;
2308 
2309     int32_t compact, n, j, m, m1;
2310     floatx80 fp0, fp1, fp2, fp3, l2, sc, onebysc;
2311 
2312     aSig = extractFloatx80Frac(a);
2313     aExp = extractFloatx80Exp(a);
2314     aSign = extractFloatx80Sign(a);
2315 
2316     if (aExp == 0x7FFF) {
2317         if ((uint64_t) (aSig << 1)) {
2318             return propagateFloatx80NaNOneArg(a, status);
2319         }
2320         if (aSign) {
2321             return packFloatx80(aSign, one_exp, one_sig);
2322         }
2323         return packFloatx80(0, floatx80_infinity.high,
2324                             floatx80_infinity.low);
2325     }
2326 
2327     if (aExp == 0 && aSig == 0) {
2328         return packFloatx80(aSign, 0, 0);
2329     }
2330 
2331     user_rnd_mode = status->float_rounding_mode;
2332     user_rnd_prec = status->floatx80_rounding_precision;
2333     status->float_rounding_mode = float_round_nearest_even;
2334     status->floatx80_rounding_precision = floatx80_precision_x;
2335 
2336     if (aExp >= 0x3FFD) { /* |X| >= 1/4 */
2337         compact = floatx80_make_compact(aExp, aSig);
2338 
2339         if (compact <= 0x4004C215) { /* |X| <= 70 log2 */
2340             fp0 = a;
2341             fp1 = a;
2342             fp0 = floatx80_mul(fp0, float32_to_floatx80(
2343                                make_float32(0x42B8AA3B), status),
2344                                status); /* 64/log2 * X */
2345             n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */
2346             fp0 = int32_to_floatx80(n, status);
2347 
2348             j = n & 0x3F; /* J = N mod 64 */
2349             m = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
2350             if (n < 0 && j) {
2351                 /*
2352                  * arithmetic right shift is division and
2353                  * round towards minus infinity
2354                  */
2355                 m--;
2356             }
2357             m1 = -m;
2358             /*m += 0x3FFF; // biased exponent of 2^(M) */
2359             /*m1 += 0x3FFF; // biased exponent of -2^(-M) */
2360 
2361             fp2 = fp0; /* N */
2362             fp0 = floatx80_mul(fp0, float32_to_floatx80(
2363                                make_float32(0xBC317218), status),
2364                                status); /* N * L1, L1 = lead(-log2/64) */
2365             l2 = packFloatx80(0, 0x3FDC, UINT64_C(0x82E308654361C4C6));
2366             fp2 = floatx80_mul(fp2, l2, status); /* N * L2, L1+L2 = -log2/64 */
2367             fp0 = floatx80_add(fp0, fp1, status); /* X + N*L1 */
2368             fp0 = floatx80_add(fp0, fp2, status); /* R */
2369 
2370             fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
2371             fp2 = float32_to_floatx80(make_float32(0x3950097B),
2372                                       status); /* A6 */
2373             fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*A6 */
2374             fp3 = floatx80_mul(float32_to_floatx80(make_float32(0x3AB60B6A),
2375                                status), fp1, status); /* fp3 is S*A5 */
2376             fp2 = floatx80_add(fp2, float64_to_floatx80(
2377                                make_float64(0x3F81111111174385), status),
2378                                status); /* fp2 IS A4+S*A6 */
2379             fp3 = floatx80_add(fp3, float64_to_floatx80(
2380                                make_float64(0x3FA5555555554F5A), status),
2381                                status); /* fp3 is A3+S*A5 */
2382             fp2 = floatx80_mul(fp2, fp1, status); /* fp2 IS S*(A4+S*A6) */
2383             fp3 = floatx80_mul(fp3, fp1, status); /* fp3 IS S*(A3+S*A5) */
2384             fp2 = floatx80_add(fp2, float64_to_floatx80(
2385                                make_float64(0x3FC5555555555555), status),
2386                                status); /* fp2 IS A2+S*(A4+S*A6) */
2387             fp3 = floatx80_add(fp3, float32_to_floatx80(
2388                                make_float32(0x3F000000), status),
2389                                status); /* fp3 IS A1+S*(A3+S*A5) */
2390             fp2 = floatx80_mul(fp2, fp1,
2391                                status); /* fp2 IS S*(A2+S*(A4+S*A6)) */
2392             fp1 = floatx80_mul(fp1, fp3,
2393                                status); /* fp1 IS S*(A1+S*(A3+S*A5)) */
2394             fp2 = floatx80_mul(fp2, fp0,
2395                                status); /* fp2 IS R*S*(A2+S*(A4+S*A6)) */
2396             fp0 = floatx80_add(fp0, fp1,
2397                                status); /* fp0 IS R+S*(A1+S*(A3+S*A5)) */
2398             fp0 = floatx80_add(fp0, fp2, status); /* fp0 IS EXP(R) - 1 */
2399 
2400             fp0 = floatx80_mul(fp0, exp_tbl[j],
2401                                status); /* 2^(J/64)*(Exp(R)-1) */
2402 
2403             if (m >= 64) {
2404                 fp1 = float32_to_floatx80(exp_tbl2[j], status);
2405                 onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */
2406                 fp1 = floatx80_add(fp1, onebysc, status);
2407                 fp0 = floatx80_add(fp0, fp1, status);
2408                 fp0 = floatx80_add(fp0, exp_tbl[j], status);
2409             } else if (m < -3) {
2410                 fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j],
2411                                    status), status);
2412                 fp0 = floatx80_add(fp0, exp_tbl[j], status);
2413                 onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */
2414                 fp0 = floatx80_add(fp0, onebysc, status);
2415             } else { /* -3 <= m <= 63 */
2416                 fp1 = exp_tbl[j];
2417                 fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j],
2418                                    status), status);
2419                 onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */
2420                 fp1 = floatx80_add(fp1, onebysc, status);
2421                 fp0 = floatx80_add(fp0, fp1, status);
2422             }
2423 
2424             sc = packFloatx80(0, m + 0x3FFF, one_sig);
2425 
2426             status->float_rounding_mode = user_rnd_mode;
2427             status->floatx80_rounding_precision = user_rnd_prec;
2428 
2429             a = floatx80_mul(fp0, sc, status);
2430 
2431             float_raise(float_flag_inexact, status);
2432 
2433             return a;
2434         } else { /* |X| > 70 log2 */
2435             if (aSign) {
2436                 fp0 = float32_to_floatx80(make_float32(0xBF800000),
2437                       status); /* -1 */
2438 
2439                 status->float_rounding_mode = user_rnd_mode;
2440                 status->floatx80_rounding_precision = user_rnd_prec;
2441 
2442                 a = floatx80_add(fp0, float32_to_floatx80(
2443                                  make_float32(0x00800000), status),
2444                                  status); /* -1 + 2^(-126) */
2445 
2446                 float_raise(float_flag_inexact, status);
2447 
2448                 return a;
2449             } else {
2450                 status->float_rounding_mode = user_rnd_mode;
2451                 status->floatx80_rounding_precision = user_rnd_prec;
2452 
2453                 return floatx80_etox(a, status);
2454             }
2455         }
2456     } else { /* |X| < 1/4 */
2457         if (aExp >= 0x3FBE) {
2458             fp0 = a;
2459             fp0 = floatx80_mul(fp0, fp0, status); /* S = X*X */
2460             fp1 = float32_to_floatx80(make_float32(0x2F30CAA8),
2461                                       status); /* B12 */
2462             fp1 = floatx80_mul(fp1, fp0, status); /* S * B12 */
2463             fp2 = float32_to_floatx80(make_float32(0x310F8290),
2464                                       status); /* B11 */
2465             fp1 = floatx80_add(fp1, float32_to_floatx80(
2466                                make_float32(0x32D73220), status),
2467                                status); /* B10 */
2468             fp2 = floatx80_mul(fp2, fp0, status);
2469             fp1 = floatx80_mul(fp1, fp0, status);
2470             fp2 = floatx80_add(fp2, float32_to_floatx80(
2471                                make_float32(0x3493F281), status),
2472                                status); /* B9 */
2473             fp1 = floatx80_add(fp1, float64_to_floatx80(
2474                                make_float64(0x3EC71DE3A5774682), status),
2475                                status); /* B8 */
2476             fp2 = floatx80_mul(fp2, fp0, status);
2477             fp1 = floatx80_mul(fp1, fp0, status);
2478             fp2 = floatx80_add(fp2, float64_to_floatx80(
2479                                make_float64(0x3EFA01A019D7CB68), status),
2480                                status); /* B7 */
2481             fp1 = floatx80_add(fp1, float64_to_floatx80(
2482                                make_float64(0x3F2A01A01A019DF3), status),
2483                                status); /* B6 */
2484             fp2 = floatx80_mul(fp2, fp0, status);
2485             fp1 = floatx80_mul(fp1, fp0, status);
2486             fp2 = floatx80_add(fp2, float64_to_floatx80(
2487                                make_float64(0x3F56C16C16C170E2), status),
2488                                status); /* B5 */
2489             fp1 = floatx80_add(fp1, float64_to_floatx80(
2490                                make_float64(0x3F81111111111111), status),
2491                                status); /* B4 */
2492             fp2 = floatx80_mul(fp2, fp0, status);
2493             fp1 = floatx80_mul(fp1, fp0, status);
2494             fp2 = floatx80_add(fp2, float64_to_floatx80(
2495                                make_float64(0x3FA5555555555555), status),
2496                                status); /* B3 */
2497             fp3 = packFloatx80(0, 0x3FFC, UINT64_C(0xAAAAAAAAAAAAAAAB));
2498             fp1 = floatx80_add(fp1, fp3, status); /* B2 */
2499             fp2 = floatx80_mul(fp2, fp0, status);
2500             fp1 = floatx80_mul(fp1, fp0, status);
2501 
2502             fp2 = floatx80_mul(fp2, fp0, status);
2503             fp1 = floatx80_mul(fp1, a, status);
2504 
2505             fp0 = floatx80_mul(fp0, float32_to_floatx80(
2506                                make_float32(0x3F000000), status),
2507                                status); /* S*B1 */
2508             fp1 = floatx80_add(fp1, fp2, status); /* Q */
2509             fp0 = floatx80_add(fp0, fp1, status); /* S*B1+Q */
2510 
2511             status->float_rounding_mode = user_rnd_mode;
2512             status->floatx80_rounding_precision = user_rnd_prec;
2513 
2514             a = floatx80_add(fp0, a, status);
2515 
2516             float_raise(float_flag_inexact, status);
2517 
2518             return a;
2519         } else { /* |X| < 2^(-65) */
2520             sc = packFloatx80(1, 1, one_sig);
2521             fp0 = a;
2522 
2523             if (aExp < 0x0033) { /* |X| < 2^(-16382) */
2524                 fp0 = floatx80_mul(fp0, float64_to_floatx80(
2525                                    make_float64(0x48B0000000000000), status),
2526                                    status);
2527                 fp0 = floatx80_add(fp0, sc, status);
2528 
2529                 status->float_rounding_mode = user_rnd_mode;
2530                 status->floatx80_rounding_precision = user_rnd_prec;
2531 
2532                 a = floatx80_mul(fp0, float64_to_floatx80(
2533                                  make_float64(0x3730000000000000), status),
2534                                  status);
2535             } else {
2536                 status->float_rounding_mode = user_rnd_mode;
2537                 status->floatx80_rounding_precision = user_rnd_prec;
2538 
2539                 a = floatx80_add(fp0, sc, status);
2540             }
2541 
2542             float_raise(float_flag_inexact, status);
2543 
2544             return a;
2545         }
2546     }
2547 }
2548 
2549 /*
2550  * Hyperbolic tangent
2551  */
2552 
2553 floatx80 floatx80_tanh(floatx80 a, float_status *status)
2554 {
2555     bool aSign, vSign;
2556     int32_t aExp, vExp;
2557     uint64_t aSig, vSig;
2558 
2559     FloatRoundMode user_rnd_mode;
2560     FloatX80RoundPrec user_rnd_prec;
2561 
2562     int32_t compact;
2563     floatx80 fp0, fp1;
2564     uint32_t sign;
2565 
2566     aSig = extractFloatx80Frac(a);
2567     aExp = extractFloatx80Exp(a);
2568     aSign = extractFloatx80Sign(a);
2569 
2570     if (aExp == 0x7FFF) {
2571         if ((uint64_t) (aSig << 1)) {
2572             return propagateFloatx80NaNOneArg(a, status);
2573         }
2574         return packFloatx80(aSign, one_exp, one_sig);
2575     }
2576 
2577     if (aExp == 0 && aSig == 0) {
2578         return packFloatx80(aSign, 0, 0);
2579     }
2580 
2581     user_rnd_mode = status->float_rounding_mode;
2582     user_rnd_prec = status->floatx80_rounding_precision;
2583     status->float_rounding_mode = float_round_nearest_even;
2584     status->floatx80_rounding_precision = floatx80_precision_x;
2585 
2586     compact = floatx80_make_compact(aExp, aSig);
2587 
2588     if (compact < 0x3FD78000 || compact > 0x3FFFDDCE) {
2589         /* TANHBORS */
2590         if (compact < 0x3FFF8000) {
2591             /* TANHSM */
2592             status->float_rounding_mode = user_rnd_mode;
2593             status->floatx80_rounding_precision = user_rnd_prec;
2594 
2595             a = floatx80_move(a, status);
2596 
2597             float_raise(float_flag_inexact, status);
2598 
2599             return a;
2600         } else {
2601             if (compact > 0x40048AA1) {
2602                 /* TANHHUGE */
2603                 sign = 0x3F800000;
2604                 sign |= aSign ? 0x80000000 : 0x00000000;
2605                 fp0 = float32_to_floatx80(make_float32(sign), status);
2606                 sign &= 0x80000000;
2607                 sign ^= 0x80800000; /* -SIGN(X)*EPS */
2608 
2609                 status->float_rounding_mode = user_rnd_mode;
2610                 status->floatx80_rounding_precision = user_rnd_prec;
2611 
2612                 a = floatx80_add(fp0, float32_to_floatx80(make_float32(sign),
2613                                  status), status);
2614 
2615                 float_raise(float_flag_inexact, status);
2616 
2617                 return a;
2618             } else {
2619                 fp0 = packFloatx80(0, aExp + 1, aSig); /* Y = 2|X| */
2620                 fp0 = floatx80_etox(fp0, status); /* FP0 IS EXP(Y) */
2621                 fp0 = floatx80_add(fp0, float32_to_floatx80(
2622                                    make_float32(0x3F800000),
2623                                    status), status); /* EXP(Y)+1 */
2624                 sign = aSign ? 0x80000000 : 0x00000000;
2625                 fp1 = floatx80_div(float32_to_floatx80(make_float32(
2626                                    sign ^ 0xC0000000), status), fp0,
2627                                    status); /* -SIGN(X)*2 / [EXP(Y)+1] */
2628                 fp0 = float32_to_floatx80(make_float32(sign | 0x3F800000),
2629                                           status); /* SIGN */
2630 
2631                 status->float_rounding_mode = user_rnd_mode;
2632                 status->floatx80_rounding_precision = user_rnd_prec;
2633 
2634                 a = floatx80_add(fp1, fp0, status);
2635 
2636                 float_raise(float_flag_inexact, status);
2637 
2638                 return a;
2639             }
2640         }
2641     } else { /* 2**(-40) < |X| < (5/2)LOG2 */
2642         fp0 = packFloatx80(0, aExp + 1, aSig); /* Y = 2|X| */
2643         fp0 = floatx80_etoxm1(fp0, status); /* FP0 IS Z = EXPM1(Y) */
2644         fp1 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x40000000),
2645                            status),
2646                            status); /* Z+2 */
2647 
2648         vSign = extractFloatx80Sign(fp1);
2649         vExp = extractFloatx80Exp(fp1);
2650         vSig = extractFloatx80Frac(fp1);
2651 
2652         fp1 = packFloatx80(vSign ^ aSign, vExp, vSig);
2653 
2654         status->float_rounding_mode = user_rnd_mode;
2655         status->floatx80_rounding_precision = user_rnd_prec;
2656 
2657         a = floatx80_div(fp0, fp1, status);
2658 
2659         float_raise(float_flag_inexact, status);
2660 
2661         return a;
2662     }
2663 }
2664 
2665 /*
2666  * Hyperbolic sine
2667  */
2668 
2669 floatx80 floatx80_sinh(floatx80 a, float_status *status)
2670 {
2671     bool aSign;
2672     int32_t aExp;
2673     uint64_t aSig;
2674 
2675     FloatRoundMode user_rnd_mode;
2676     FloatX80RoundPrec user_rnd_prec;
2677 
2678     int32_t compact;
2679     floatx80 fp0, fp1, fp2;
2680     float32 fact;
2681 
2682     aSig = extractFloatx80Frac(a);
2683     aExp = extractFloatx80Exp(a);
2684     aSign = extractFloatx80Sign(a);
2685 
2686     if (aExp == 0x7FFF) {
2687         if ((uint64_t) (aSig << 1)) {
2688             return propagateFloatx80NaNOneArg(a, status);
2689         }
2690         return packFloatx80(aSign, floatx80_infinity.high,
2691                             floatx80_infinity.low);
2692     }
2693 
2694     if (aExp == 0 && aSig == 0) {
2695         return packFloatx80(aSign, 0, 0);
2696     }
2697 
2698     user_rnd_mode = status->float_rounding_mode;
2699     user_rnd_prec = status->floatx80_rounding_precision;
2700     status->float_rounding_mode = float_round_nearest_even;
2701     status->floatx80_rounding_precision = floatx80_precision_x;
2702 
2703     compact = floatx80_make_compact(aExp, aSig);
2704 
2705     if (compact > 0x400CB167) {
2706         /* SINHBIG */
2707         if (compact > 0x400CB2B3) {
2708             status->float_rounding_mode = user_rnd_mode;
2709             status->floatx80_rounding_precision = user_rnd_prec;
2710 
2711             return roundAndPackFloatx80(status->floatx80_rounding_precision,
2712                                         aSign, 0x8000, aSig, 0, status);
2713         } else {
2714             fp0 = floatx80_abs(a); /* Y = |X| */
2715             fp0 = floatx80_sub(fp0, float64_to_floatx80(
2716                                make_float64(0x40C62D38D3D64634), status),
2717                                status); /* (|X|-16381LOG2_LEAD) */
2718             fp0 = floatx80_sub(fp0, float64_to_floatx80(
2719                                make_float64(0x3D6F90AEB1E75CC7), status),
2720                                status); /* |X| - 16381 LOG2, ACCURATE */
2721             fp0 = floatx80_etox(fp0, status);
2722             fp2 = packFloatx80(aSign, 0x7FFB, one_sig);
2723 
2724             status->float_rounding_mode = user_rnd_mode;
2725             status->floatx80_rounding_precision = user_rnd_prec;
2726 
2727             a = floatx80_mul(fp0, fp2, status);
2728 
2729             float_raise(float_flag_inexact, status);
2730 
2731             return a;
2732         }
2733     } else { /* |X| < 16380 LOG2 */
2734         fp0 = floatx80_abs(a); /* Y = |X| */
2735         fp0 = floatx80_etoxm1(fp0, status); /* FP0 IS Z = EXPM1(Y) */
2736         fp1 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
2737                            status), status); /* 1+Z */
2738         fp2 = fp0;
2739         fp0 = floatx80_div(fp0, fp1, status); /* Z/(1+Z) */
2740         fp0 = floatx80_add(fp0, fp2, status);
2741 
2742         fact = packFloat32(aSign, 0x7E, 0);
2743 
2744         status->float_rounding_mode = user_rnd_mode;
2745         status->floatx80_rounding_precision = user_rnd_prec;
2746 
2747         a = floatx80_mul(fp0, float32_to_floatx80(fact, status), status);
2748 
2749         float_raise(float_flag_inexact, status);
2750 
2751         return a;
2752     }
2753 }
2754 
2755 /*
2756  * Hyperbolic cosine
2757  */
2758 
2759 floatx80 floatx80_cosh(floatx80 a, float_status *status)
2760 {
2761     int32_t aExp;
2762     uint64_t aSig;
2763 
2764     FloatRoundMode user_rnd_mode;
2765     FloatX80RoundPrec user_rnd_prec;
2766 
2767     int32_t compact;
2768     floatx80 fp0, fp1;
2769 
2770     aSig = extractFloatx80Frac(a);
2771     aExp = extractFloatx80Exp(a);
2772 
2773     if (aExp == 0x7FFF) {
2774         if ((uint64_t) (aSig << 1)) {
2775             return propagateFloatx80NaNOneArg(a, status);
2776         }
2777         return packFloatx80(0, floatx80_infinity.high,
2778                             floatx80_infinity.low);
2779     }
2780 
2781     if (aExp == 0 && aSig == 0) {
2782         return packFloatx80(0, one_exp, one_sig);
2783     }
2784 
2785     user_rnd_mode = status->float_rounding_mode;
2786     user_rnd_prec = status->floatx80_rounding_precision;
2787     status->float_rounding_mode = float_round_nearest_even;
2788     status->floatx80_rounding_precision = floatx80_precision_x;
2789 
2790     compact = floatx80_make_compact(aExp, aSig);
2791 
2792     if (compact > 0x400CB167) {
2793         if (compact > 0x400CB2B3) {
2794             status->float_rounding_mode = user_rnd_mode;
2795             status->floatx80_rounding_precision = user_rnd_prec;
2796             return roundAndPackFloatx80(status->floatx80_rounding_precision, 0,
2797                                         0x8000, one_sig, 0, status);
2798         } else {
2799             fp0 = packFloatx80(0, aExp, aSig);
2800             fp0 = floatx80_sub(fp0, float64_to_floatx80(
2801                                make_float64(0x40C62D38D3D64634), status),
2802                                status);
2803             fp0 = floatx80_sub(fp0, float64_to_floatx80(
2804                                make_float64(0x3D6F90AEB1E75CC7), status),
2805                                status);
2806             fp0 = floatx80_etox(fp0, status);
2807             fp1 = packFloatx80(0, 0x7FFB, one_sig);
2808 
2809             status->float_rounding_mode = user_rnd_mode;
2810             status->floatx80_rounding_precision = user_rnd_prec;
2811 
2812             a = floatx80_mul(fp0, fp1, status);
2813 
2814             float_raise(float_flag_inexact, status);
2815 
2816             return a;
2817         }
2818     }
2819 
2820     fp0 = packFloatx80(0, aExp, aSig); /* |X| */
2821     fp0 = floatx80_etox(fp0, status); /* EXP(|X|) */
2822     fp0 = floatx80_mul(fp0, float32_to_floatx80(make_float32(0x3F000000),
2823                        status), status); /* (1/2)*EXP(|X|) */
2824     fp1 = float32_to_floatx80(make_float32(0x3E800000), status); /* 1/4 */
2825     fp1 = floatx80_div(fp1, fp0, status); /* 1/(2*EXP(|X|)) */
2826 
2827     status->float_rounding_mode = user_rnd_mode;
2828     status->floatx80_rounding_precision = user_rnd_prec;
2829 
2830     a = floatx80_add(fp0, fp1, status);
2831 
2832     float_raise(float_flag_inexact, status);
2833 
2834     return a;
2835 }
2836