xref: /openbmc/qemu/target/m68k/softfloat.c (revision 068f1615)
1591596b7SLaurent Vivier /*
2591596b7SLaurent Vivier  * Ported from a work by Andreas Grabher for Previous, NeXT Computer Emulator,
3591596b7SLaurent Vivier  * derived from NetBSD M68040 FPSP functions,
4591596b7SLaurent Vivier  * derived from release 2a of the SoftFloat IEC/IEEE Floating-point Arithmetic
5591596b7SLaurent Vivier  * Package. Those parts of the code (and some later contributions) are
6591596b7SLaurent Vivier  * provided under that license, as detailed below.
7591596b7SLaurent Vivier  * It has subsequently been modified by contributors to the QEMU Project,
8591596b7SLaurent Vivier  * so some portions are provided under:
9591596b7SLaurent Vivier  *  the SoftFloat-2a license
10591596b7SLaurent Vivier  *  the BSD license
11591596b7SLaurent Vivier  *  GPL-v2-or-later
12591596b7SLaurent Vivier  *
13591596b7SLaurent Vivier  * Any future contributions to this file will be taken to be licensed under
14591596b7SLaurent Vivier  * the Softfloat-2a license unless specifically indicated otherwise.
15591596b7SLaurent Vivier  */
16591596b7SLaurent Vivier 
17591596b7SLaurent Vivier /* Portions of this work are licensed under the terms of the GNU GPL,
18591596b7SLaurent Vivier  * version 2 or later. See the COPYING file in the top-level directory.
19591596b7SLaurent Vivier  */
20591596b7SLaurent Vivier 
21591596b7SLaurent Vivier #include "qemu/osdep.h"
22591596b7SLaurent Vivier #include "softfloat.h"
23591596b7SLaurent Vivier #include "fpu/softfloat-macros.h"
244b5c65b8SLaurent Vivier #include "softfloat_fpsp_tables.h"
25591596b7SLaurent Vivier 
260d379c17SLaurent Vivier static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status)
270d379c17SLaurent Vivier {
280d379c17SLaurent Vivier     if (floatx80_is_signaling_nan(a, status)) {
290d379c17SLaurent Vivier         float_raise(float_flag_invalid, status);
300d379c17SLaurent Vivier     }
310d379c17SLaurent Vivier 
320d379c17SLaurent Vivier     if (status->default_nan_mode) {
330d379c17SLaurent Vivier         return floatx80_default_nan(status);
340d379c17SLaurent Vivier     }
350d379c17SLaurent Vivier 
360d379c17SLaurent Vivier     return floatx80_maybe_silence_nan(a, status);
370d379c17SLaurent Vivier }
380d379c17SLaurent Vivier 
39591596b7SLaurent Vivier /*----------------------------------------------------------------------------
40591596b7SLaurent Vivier  | Returns the modulo remainder of the extended double-precision floating-point
41591596b7SLaurent Vivier  | value `a' with respect to the corresponding value `b'.
42591596b7SLaurent Vivier  *----------------------------------------------------------------------------*/
43591596b7SLaurent Vivier 
44591596b7SLaurent Vivier floatx80 floatx80_mod(floatx80 a, floatx80 b, float_status *status)
45591596b7SLaurent Vivier {
46591596b7SLaurent Vivier     flag aSign, zSign;
47591596b7SLaurent Vivier     int32_t aExp, bExp, expDiff;
48591596b7SLaurent Vivier     uint64_t aSig0, aSig1, bSig;
49591596b7SLaurent Vivier     uint64_t qTemp, term0, term1;
50591596b7SLaurent Vivier 
51591596b7SLaurent Vivier     aSig0 = extractFloatx80Frac(a);
52591596b7SLaurent Vivier     aExp = extractFloatx80Exp(a);
53591596b7SLaurent Vivier     aSign = extractFloatx80Sign(a);
54591596b7SLaurent Vivier     bSig = extractFloatx80Frac(b);
55591596b7SLaurent Vivier     bExp = extractFloatx80Exp(b);
56591596b7SLaurent Vivier 
57591596b7SLaurent Vivier     if (aExp == 0x7FFF) {
58591596b7SLaurent Vivier         if ((uint64_t) (aSig0 << 1)
59591596b7SLaurent Vivier             || ((bExp == 0x7FFF) && (uint64_t) (bSig << 1))) {
60591596b7SLaurent Vivier             return propagateFloatx80NaN(a, b, status);
61591596b7SLaurent Vivier         }
62591596b7SLaurent Vivier         goto invalid;
63591596b7SLaurent Vivier     }
64591596b7SLaurent Vivier     if (bExp == 0x7FFF) {
65591596b7SLaurent Vivier         if ((uint64_t) (bSig << 1)) {
66591596b7SLaurent Vivier             return propagateFloatx80NaN(a, b, status);
67591596b7SLaurent Vivier         }
68591596b7SLaurent Vivier         return a;
69591596b7SLaurent Vivier     }
70591596b7SLaurent Vivier     if (bExp == 0) {
71591596b7SLaurent Vivier         if (bSig == 0) {
72591596b7SLaurent Vivier         invalid:
73591596b7SLaurent Vivier             float_raise(float_flag_invalid, status);
74591596b7SLaurent Vivier             return floatx80_default_nan(status);
75591596b7SLaurent Vivier         }
76591596b7SLaurent Vivier         normalizeFloatx80Subnormal(bSig, &bExp, &bSig);
77591596b7SLaurent Vivier     }
78591596b7SLaurent Vivier     if (aExp == 0) {
79591596b7SLaurent Vivier         if ((uint64_t) (aSig0 << 1) == 0) {
80591596b7SLaurent Vivier             return a;
81591596b7SLaurent Vivier         }
82591596b7SLaurent Vivier         normalizeFloatx80Subnormal(aSig0, &aExp, &aSig0);
83591596b7SLaurent Vivier     }
84591596b7SLaurent Vivier     bSig |= LIT64(0x8000000000000000);
85591596b7SLaurent Vivier     zSign = aSign;
86591596b7SLaurent Vivier     expDiff = aExp - bExp;
87591596b7SLaurent Vivier     aSig1 = 0;
88591596b7SLaurent Vivier     if (expDiff < 0) {
89591596b7SLaurent Vivier         return a;
90591596b7SLaurent Vivier     }
91591596b7SLaurent Vivier     qTemp = (bSig <= aSig0);
92591596b7SLaurent Vivier     if (qTemp) {
93591596b7SLaurent Vivier         aSig0 -= bSig;
94591596b7SLaurent Vivier     }
95591596b7SLaurent Vivier     expDiff -= 64;
96591596b7SLaurent Vivier     while (0 < expDiff) {
97591596b7SLaurent Vivier         qTemp = estimateDiv128To64(aSig0, aSig1, bSig);
98591596b7SLaurent Vivier         qTemp = (2 < qTemp) ? qTemp - 2 : 0;
99591596b7SLaurent Vivier         mul64To128(bSig, qTemp, &term0, &term1);
100591596b7SLaurent Vivier         sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1);
101591596b7SLaurent Vivier         shortShift128Left(aSig0, aSig1, 62, &aSig0, &aSig1);
102591596b7SLaurent Vivier     }
103591596b7SLaurent Vivier     expDiff += 64;
104591596b7SLaurent Vivier     if (0 < expDiff) {
105591596b7SLaurent Vivier         qTemp = estimateDiv128To64(aSig0, aSig1, bSig);
106591596b7SLaurent Vivier         qTemp = (2 < qTemp) ? qTemp - 2 : 0;
107591596b7SLaurent Vivier         qTemp >>= 64 - expDiff;
108591596b7SLaurent Vivier         mul64To128(bSig, qTemp << (64 - expDiff), &term0, &term1);
109591596b7SLaurent Vivier         sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1);
110591596b7SLaurent Vivier         shortShift128Left(0, bSig, 64 - expDiff, &term0, &term1);
111591596b7SLaurent Vivier         while (le128(term0, term1, aSig0, aSig1)) {
112591596b7SLaurent Vivier             ++qTemp;
113591596b7SLaurent Vivier             sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1);
114591596b7SLaurent Vivier         }
115591596b7SLaurent Vivier     }
116591596b7SLaurent Vivier     return
117591596b7SLaurent Vivier         normalizeRoundAndPackFloatx80(
118591596b7SLaurent Vivier             80, zSign, bExp + expDiff, aSig0, aSig1, status);
119591596b7SLaurent Vivier }
1200d379c17SLaurent Vivier 
1210d379c17SLaurent Vivier /*----------------------------------------------------------------------------
1220d379c17SLaurent Vivier  | Returns the mantissa of the extended double-precision floating-point
1230d379c17SLaurent Vivier  | value `a'.
1240d379c17SLaurent Vivier  *----------------------------------------------------------------------------*/
1250d379c17SLaurent Vivier 
1260d379c17SLaurent Vivier floatx80 floatx80_getman(floatx80 a, float_status *status)
1270d379c17SLaurent Vivier {
1280d379c17SLaurent Vivier     flag aSign;
1290d379c17SLaurent Vivier     int32_t aExp;
1300d379c17SLaurent Vivier     uint64_t aSig;
1310d379c17SLaurent Vivier 
1320d379c17SLaurent Vivier     aSig = extractFloatx80Frac(a);
1330d379c17SLaurent Vivier     aExp = extractFloatx80Exp(a);
1340d379c17SLaurent Vivier     aSign = extractFloatx80Sign(a);
1350d379c17SLaurent Vivier 
1360d379c17SLaurent Vivier     if (aExp == 0x7FFF) {
1370d379c17SLaurent Vivier         if ((uint64_t) (aSig << 1)) {
1380d379c17SLaurent Vivier             return propagateFloatx80NaNOneArg(a , status);
1390d379c17SLaurent Vivier         }
1400d379c17SLaurent Vivier         float_raise(float_flag_invalid , status);
1410d379c17SLaurent Vivier         return floatx80_default_nan(status);
1420d379c17SLaurent Vivier     }
1430d379c17SLaurent Vivier 
1440d379c17SLaurent Vivier     if (aExp == 0) {
1450d379c17SLaurent Vivier         if (aSig == 0) {
1460d379c17SLaurent Vivier             return packFloatx80(aSign, 0, 0);
1470d379c17SLaurent Vivier         }
1480d379c17SLaurent Vivier         normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
1490d379c17SLaurent Vivier     }
1500d379c17SLaurent Vivier 
1510d379c17SLaurent Vivier     return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign,
1520d379c17SLaurent Vivier                                 0x3FFF, aSig, 0, status);
1530d379c17SLaurent Vivier }
1540d379c17SLaurent Vivier 
1550d379c17SLaurent Vivier /*----------------------------------------------------------------------------
1560d379c17SLaurent Vivier  | Returns the exponent of the extended double-precision floating-point
1570d379c17SLaurent Vivier  | value `a' as an extended double-precision value.
1580d379c17SLaurent Vivier  *----------------------------------------------------------------------------*/
1590d379c17SLaurent Vivier 
1600d379c17SLaurent Vivier floatx80 floatx80_getexp(floatx80 a, float_status *status)
1610d379c17SLaurent Vivier {
1620d379c17SLaurent Vivier     flag aSign;
1630d379c17SLaurent Vivier     int32_t aExp;
1640d379c17SLaurent Vivier     uint64_t aSig;
1650d379c17SLaurent Vivier 
1660d379c17SLaurent Vivier     aSig = extractFloatx80Frac(a);
1670d379c17SLaurent Vivier     aExp = extractFloatx80Exp(a);
1680d379c17SLaurent Vivier     aSign = extractFloatx80Sign(a);
1690d379c17SLaurent Vivier 
1700d379c17SLaurent Vivier     if (aExp == 0x7FFF) {
1710d379c17SLaurent Vivier         if ((uint64_t) (aSig << 1)) {
1720d379c17SLaurent Vivier             return propagateFloatx80NaNOneArg(a , status);
1730d379c17SLaurent Vivier         }
1740d379c17SLaurent Vivier         float_raise(float_flag_invalid , status);
1750d379c17SLaurent Vivier         return floatx80_default_nan(status);
1760d379c17SLaurent Vivier     }
1770d379c17SLaurent Vivier 
1780d379c17SLaurent Vivier     if (aExp == 0) {
1790d379c17SLaurent Vivier         if (aSig == 0) {
1800d379c17SLaurent Vivier             return packFloatx80(aSign, 0, 0);
1810d379c17SLaurent Vivier         }
1820d379c17SLaurent Vivier         normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
1830d379c17SLaurent Vivier     }
1840d379c17SLaurent Vivier 
1850d379c17SLaurent Vivier     return int32_to_floatx80(aExp - 0x3FFF, status);
1860d379c17SLaurent Vivier }
1870d379c17SLaurent Vivier 
1880d379c17SLaurent Vivier /*----------------------------------------------------------------------------
1890d379c17SLaurent Vivier  | Scales extended double-precision floating-point value in operand `a' by
1900d379c17SLaurent Vivier  | value `b'. The function truncates the value in the second operand 'b' to
1910d379c17SLaurent Vivier  | an integral value and adds that value to the exponent of the operand 'a'.
1920d379c17SLaurent Vivier  | The operation performed according to the IEC/IEEE Standard for Binary
1930d379c17SLaurent Vivier  | Floating-Point Arithmetic.
1940d379c17SLaurent Vivier  *----------------------------------------------------------------------------*/
1950d379c17SLaurent Vivier 
1960d379c17SLaurent Vivier floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status)
1970d379c17SLaurent Vivier {
1980d379c17SLaurent Vivier     flag aSign, bSign;
1990d379c17SLaurent Vivier     int32_t aExp, bExp, shiftCount;
2000d379c17SLaurent Vivier     uint64_t aSig, bSig;
2010d379c17SLaurent Vivier 
2020d379c17SLaurent Vivier     aSig = extractFloatx80Frac(a);
2030d379c17SLaurent Vivier     aExp = extractFloatx80Exp(a);
2040d379c17SLaurent Vivier     aSign = extractFloatx80Sign(a);
2050d379c17SLaurent Vivier     bSig = extractFloatx80Frac(b);
2060d379c17SLaurent Vivier     bExp = extractFloatx80Exp(b);
2070d379c17SLaurent Vivier     bSign = extractFloatx80Sign(b);
2080d379c17SLaurent Vivier 
2090d379c17SLaurent Vivier     if (bExp == 0x7FFF) {
2100d379c17SLaurent Vivier         if ((uint64_t) (bSig << 1) ||
2110d379c17SLaurent Vivier             ((aExp == 0x7FFF) && (uint64_t) (aSig << 1))) {
2120d379c17SLaurent Vivier             return propagateFloatx80NaN(a, b, status);
2130d379c17SLaurent Vivier         }
2140d379c17SLaurent Vivier         float_raise(float_flag_invalid , status);
2150d379c17SLaurent Vivier         return floatx80_default_nan(status);
2160d379c17SLaurent Vivier     }
2170d379c17SLaurent Vivier     if (aExp == 0x7FFF) {
2180d379c17SLaurent Vivier         if ((uint64_t) (aSig << 1)) {
2190d379c17SLaurent Vivier             return propagateFloatx80NaN(a, b, status);
2200d379c17SLaurent Vivier         }
2210d379c17SLaurent Vivier         return packFloatx80(aSign, floatx80_infinity.high,
2220d379c17SLaurent Vivier                             floatx80_infinity.low);
2230d379c17SLaurent Vivier     }
2240d379c17SLaurent Vivier     if (aExp == 0) {
2250d379c17SLaurent Vivier         if (aSig == 0) {
2260d379c17SLaurent Vivier             return packFloatx80(aSign, 0, 0);
2270d379c17SLaurent Vivier         }
2280d379c17SLaurent Vivier         if (bExp < 0x3FFF) {
2290d379c17SLaurent Vivier             return a;
2300d379c17SLaurent Vivier         }
2310d379c17SLaurent Vivier         normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
2320d379c17SLaurent Vivier     }
2330d379c17SLaurent Vivier 
2340d379c17SLaurent Vivier     if (bExp < 0x3FFF) {
2350d379c17SLaurent Vivier         return a;
2360d379c17SLaurent Vivier     }
2370d379c17SLaurent Vivier 
2380d379c17SLaurent Vivier     if (0x400F < bExp) {
2390d379c17SLaurent Vivier         aExp = bSign ? -0x6001 : 0xE000;
2400d379c17SLaurent Vivier         return roundAndPackFloatx80(status->floatx80_rounding_precision,
2410d379c17SLaurent Vivier                                     aSign, aExp, aSig, 0, status);
2420d379c17SLaurent Vivier     }
2430d379c17SLaurent Vivier 
2440d379c17SLaurent Vivier     shiftCount = 0x403E - bExp;
2450d379c17SLaurent Vivier     bSig >>= shiftCount;
2460d379c17SLaurent Vivier     aExp = bSign ? (aExp - bSig) : (aExp + bSig);
2470d379c17SLaurent Vivier 
2480d379c17SLaurent Vivier     return roundAndPackFloatx80(status->floatx80_rounding_precision,
2490d379c17SLaurent Vivier                                 aSign, aExp, aSig, 0, status);
2500d379c17SLaurent Vivier }
2519a069775SLaurent Vivier 
2529a069775SLaurent Vivier floatx80 floatx80_move(floatx80 a, float_status *status)
2539a069775SLaurent Vivier {
2549a069775SLaurent Vivier     flag aSign;
2559a069775SLaurent Vivier     int32_t aExp;
2569a069775SLaurent Vivier     uint64_t aSig;
2579a069775SLaurent Vivier 
2589a069775SLaurent Vivier     aSig = extractFloatx80Frac(a);
2599a069775SLaurent Vivier     aExp = extractFloatx80Exp(a);
2609a069775SLaurent Vivier     aSign = extractFloatx80Sign(a);
2619a069775SLaurent Vivier 
2629a069775SLaurent Vivier     if (aExp == 0x7FFF) {
2639a069775SLaurent Vivier         if ((uint64_t)(aSig << 1)) {
2649a069775SLaurent Vivier             return propagateFloatx80NaNOneArg(a, status);
2659a069775SLaurent Vivier         }
2669a069775SLaurent Vivier         return a;
2679a069775SLaurent Vivier     }
2689a069775SLaurent Vivier     if (aExp == 0) {
2699a069775SLaurent Vivier         if (aSig == 0) {
2709a069775SLaurent Vivier             return a;
2719a069775SLaurent Vivier         }
2729a069775SLaurent Vivier         normalizeRoundAndPackFloatx80(status->floatx80_rounding_precision,
2739a069775SLaurent Vivier                                       aSign, aExp, aSig, 0, status);
2749a069775SLaurent Vivier     }
2759a069775SLaurent Vivier     return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign,
2769a069775SLaurent Vivier                                 aExp, aSig, 0, status);
2779a069775SLaurent Vivier }
2784b5c65b8SLaurent Vivier 
2794b5c65b8SLaurent Vivier /*----------------------------------------------------------------------------
2804b5c65b8SLaurent Vivier | Algorithms for transcendental functions supported by MC68881 and MC68882
2814b5c65b8SLaurent Vivier | mathematical coprocessors. The functions are derived from FPSP library.
2824b5c65b8SLaurent Vivier *----------------------------------------------------------------------------*/
2834b5c65b8SLaurent Vivier 
2844b5c65b8SLaurent Vivier #define one_exp     0x3FFF
2854b5c65b8SLaurent Vivier #define one_sig     LIT64(0x8000000000000000)
2864b5c65b8SLaurent Vivier 
2874b5c65b8SLaurent Vivier /*----------------------------------------------------------------------------
2884b5c65b8SLaurent Vivier  | Function for compactifying extended double-precision floating point values.
2894b5c65b8SLaurent Vivier  *----------------------------------------------------------------------------*/
2904b5c65b8SLaurent Vivier 
2914b5c65b8SLaurent Vivier static int32_t floatx80_make_compact(int32_t aExp, uint64_t aSig)
2924b5c65b8SLaurent Vivier {
2934b5c65b8SLaurent Vivier     return (aExp << 16) | (aSig >> 48);
2944b5c65b8SLaurent Vivier }
2954b5c65b8SLaurent Vivier 
2964b5c65b8SLaurent Vivier /*----------------------------------------------------------------------------
2974b5c65b8SLaurent Vivier  | Log base e of x plus 1
2984b5c65b8SLaurent Vivier  *----------------------------------------------------------------------------*/
2994b5c65b8SLaurent Vivier 
3004b5c65b8SLaurent Vivier floatx80 floatx80_lognp1(floatx80 a, float_status *status)
3014b5c65b8SLaurent Vivier {
3024b5c65b8SLaurent Vivier     flag aSign;
3034b5c65b8SLaurent Vivier     int32_t aExp;
3044b5c65b8SLaurent Vivier     uint64_t aSig, fSig;
3054b5c65b8SLaurent Vivier 
3064b5c65b8SLaurent Vivier     int8_t user_rnd_mode, user_rnd_prec;
3074b5c65b8SLaurent Vivier 
3084b5c65b8SLaurent Vivier     int32_t compact, j, k;
3094b5c65b8SLaurent Vivier     floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu;
3104b5c65b8SLaurent Vivier 
3114b5c65b8SLaurent Vivier     aSig = extractFloatx80Frac(a);
3124b5c65b8SLaurent Vivier     aExp = extractFloatx80Exp(a);
3134b5c65b8SLaurent Vivier     aSign = extractFloatx80Sign(a);
3144b5c65b8SLaurent Vivier 
3154b5c65b8SLaurent Vivier     if (aExp == 0x7FFF) {
3164b5c65b8SLaurent Vivier         if ((uint64_t) (aSig << 1)) {
3174b5c65b8SLaurent Vivier             propagateFloatx80NaNOneArg(a, status);
3184b5c65b8SLaurent Vivier         }
3194b5c65b8SLaurent Vivier         if (aSign) {
3204b5c65b8SLaurent Vivier             float_raise(float_flag_invalid, status);
3214b5c65b8SLaurent Vivier             return floatx80_default_nan(status);
3224b5c65b8SLaurent Vivier         }
3234b5c65b8SLaurent Vivier         return packFloatx80(0, floatx80_infinity.high, floatx80_infinity.low);
3244b5c65b8SLaurent Vivier     }
3254b5c65b8SLaurent Vivier 
3264b5c65b8SLaurent Vivier     if (aExp == 0 && aSig == 0) {
3274b5c65b8SLaurent Vivier         return packFloatx80(aSign, 0, 0);
3284b5c65b8SLaurent Vivier     }
3294b5c65b8SLaurent Vivier 
3304b5c65b8SLaurent Vivier     if (aSign && aExp >= one_exp) {
3314b5c65b8SLaurent Vivier         if (aExp == one_exp && aSig == one_sig) {
3324b5c65b8SLaurent Vivier             float_raise(float_flag_divbyzero, status);
3334b5c65b8SLaurent Vivier             packFloatx80(aSign, floatx80_infinity.high, floatx80_infinity.low);
3344b5c65b8SLaurent Vivier         }
3354b5c65b8SLaurent Vivier         float_raise(float_flag_invalid, status);
3364b5c65b8SLaurent Vivier         return floatx80_default_nan(status);
3374b5c65b8SLaurent Vivier     }
3384b5c65b8SLaurent Vivier 
3394b5c65b8SLaurent Vivier     if (aExp < 0x3f99 || (aExp == 0x3f99 && aSig == one_sig)) {
3404b5c65b8SLaurent Vivier         /* <= min threshold */
3414b5c65b8SLaurent Vivier         float_raise(float_flag_inexact, status);
3424b5c65b8SLaurent Vivier         return floatx80_move(a, status);
3434b5c65b8SLaurent Vivier     }
3444b5c65b8SLaurent Vivier 
3454b5c65b8SLaurent Vivier     user_rnd_mode = status->float_rounding_mode;
3464b5c65b8SLaurent Vivier     user_rnd_prec = status->floatx80_rounding_precision;
3474b5c65b8SLaurent Vivier     status->float_rounding_mode = float_round_nearest_even;
3484b5c65b8SLaurent Vivier     status->floatx80_rounding_precision = 80;
3494b5c65b8SLaurent Vivier 
3504b5c65b8SLaurent Vivier     compact = floatx80_make_compact(aExp, aSig);
3514b5c65b8SLaurent Vivier 
3524b5c65b8SLaurent Vivier     fp0 = a; /* Z */
3534b5c65b8SLaurent Vivier     fp1 = a;
3544b5c65b8SLaurent Vivier 
3554b5c65b8SLaurent Vivier     fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
3564b5c65b8SLaurent Vivier                        status), status); /* X = (1+Z) */
3574b5c65b8SLaurent Vivier 
3584b5c65b8SLaurent Vivier     aExp = extractFloatx80Exp(fp0);
3594b5c65b8SLaurent Vivier     aSig = extractFloatx80Frac(fp0);
3604b5c65b8SLaurent Vivier 
3614b5c65b8SLaurent Vivier     compact = floatx80_make_compact(aExp, aSig);
3624b5c65b8SLaurent Vivier 
3634b5c65b8SLaurent Vivier     if (compact < 0x3FFE8000 || compact > 0x3FFFC000) {
3644b5c65b8SLaurent Vivier         /* |X| < 1/2 or |X| > 3/2 */
3654b5c65b8SLaurent Vivier         k = aExp - 0x3FFF;
3664b5c65b8SLaurent Vivier         fp1 = int32_to_floatx80(k, status);
3674b5c65b8SLaurent Vivier 
3684b5c65b8SLaurent Vivier         fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000);
3694b5c65b8SLaurent Vivier         j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
3704b5c65b8SLaurent Vivier 
3714b5c65b8SLaurent Vivier         f = packFloatx80(0, 0x3FFF, fSig); /* F */
3724b5c65b8SLaurent Vivier         fp0 = packFloatx80(0, 0x3FFF, aSig); /* Y */
3734b5c65b8SLaurent Vivier 
3744b5c65b8SLaurent Vivier         fp0 = floatx80_sub(fp0, f, status); /* Y-F */
3754b5c65b8SLaurent Vivier 
3764b5c65b8SLaurent Vivier     lp1cont1:
3774b5c65b8SLaurent Vivier         /* LP1CONT1 */
3784b5c65b8SLaurent Vivier         fp0 = floatx80_mul(fp0, log_tbl[j], status); /* FP0 IS U = (Y-F)/F */
3794b5c65b8SLaurent Vivier         logof2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC));
3804b5c65b8SLaurent Vivier         klog2 = floatx80_mul(fp1, logof2, status); /* FP1 IS K*LOG2 */
3814b5c65b8SLaurent Vivier         fp2 = floatx80_mul(fp0, fp0, status); /* FP2 IS V=U*U */
3824b5c65b8SLaurent Vivier 
3834b5c65b8SLaurent Vivier         fp3 = fp2;
3844b5c65b8SLaurent Vivier         fp1 = fp2;
3854b5c65b8SLaurent Vivier 
3864b5c65b8SLaurent Vivier         fp1 = floatx80_mul(fp1, float64_to_floatx80(
3874b5c65b8SLaurent Vivier                            make_float64(0x3FC2499AB5E4040B), status),
3884b5c65b8SLaurent Vivier                            status); /* V*A6 */
3894b5c65b8SLaurent Vivier         fp2 = floatx80_mul(fp2, float64_to_floatx80(
3904b5c65b8SLaurent Vivier                            make_float64(0xBFC555B5848CB7DB), status),
3914b5c65b8SLaurent Vivier                            status); /* V*A5 */
3924b5c65b8SLaurent Vivier         fp1 = floatx80_add(fp1, float64_to_floatx80(
3934b5c65b8SLaurent Vivier                            make_float64(0x3FC99999987D8730), status),
3944b5c65b8SLaurent Vivier                            status); /* A4+V*A6 */
3954b5c65b8SLaurent Vivier         fp2 = floatx80_add(fp2, float64_to_floatx80(
3964b5c65b8SLaurent Vivier                            make_float64(0xBFCFFFFFFF6F7E97), status),
3974b5c65b8SLaurent Vivier                            status); /* A3+V*A5 */
3984b5c65b8SLaurent Vivier         fp1 = floatx80_mul(fp1, fp3, status); /* V*(A4+V*A6) */
3994b5c65b8SLaurent Vivier         fp2 = floatx80_mul(fp2, fp3, status); /* V*(A3+V*A5) */
4004b5c65b8SLaurent Vivier         fp1 = floatx80_add(fp1, float64_to_floatx80(
4014b5c65b8SLaurent Vivier                            make_float64(0x3FD55555555555A4), status),
4024b5c65b8SLaurent Vivier                            status); /* A2+V*(A4+V*A6) */
4034b5c65b8SLaurent Vivier         fp2 = floatx80_add(fp2, float64_to_floatx80(
4044b5c65b8SLaurent Vivier                            make_float64(0xBFE0000000000008), status),
4054b5c65b8SLaurent Vivier                            status); /* A1+V*(A3+V*A5) */
4064b5c65b8SLaurent Vivier         fp1 = floatx80_mul(fp1, fp3, status); /* V*(A2+V*(A4+V*A6)) */
4074b5c65b8SLaurent Vivier         fp2 = floatx80_mul(fp2, fp3, status); /* V*(A1+V*(A3+V*A5)) */
4084b5c65b8SLaurent Vivier         fp1 = floatx80_mul(fp1, fp0, status); /* U*V*(A2+V*(A4+V*A6)) */
4094b5c65b8SLaurent Vivier         fp0 = floatx80_add(fp0, fp2, status); /* U+V*(A1+V*(A3+V*A5)) */
4104b5c65b8SLaurent Vivier 
4114b5c65b8SLaurent Vivier         fp1 = floatx80_add(fp1, log_tbl[j + 1],
4124b5c65b8SLaurent Vivier                            status); /* LOG(F)+U*V*(A2+V*(A4+V*A6)) */
4134b5c65b8SLaurent Vivier         fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS LOG(F) + LOG(1+U) */
4144b5c65b8SLaurent Vivier 
4154b5c65b8SLaurent Vivier         status->float_rounding_mode = user_rnd_mode;
4164b5c65b8SLaurent Vivier         status->floatx80_rounding_precision = user_rnd_prec;
4174b5c65b8SLaurent Vivier 
4184b5c65b8SLaurent Vivier         a = floatx80_add(fp0, klog2, status);
4194b5c65b8SLaurent Vivier 
4204b5c65b8SLaurent Vivier         float_raise(float_flag_inexact, status);
4214b5c65b8SLaurent Vivier 
4224b5c65b8SLaurent Vivier         return a;
4234b5c65b8SLaurent Vivier     } else if (compact < 0x3FFEF07D || compact > 0x3FFF8841) {
4244b5c65b8SLaurent Vivier         /* |X| < 1/16 or |X| > -1/16 */
4254b5c65b8SLaurent Vivier         /* LP1CARE */
4264b5c65b8SLaurent Vivier         fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000);
4274b5c65b8SLaurent Vivier         f = packFloatx80(0, 0x3FFF, fSig); /* F */
4284b5c65b8SLaurent Vivier         j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
4294b5c65b8SLaurent Vivier 
4304b5c65b8SLaurent Vivier         if (compact >= 0x3FFF8000) { /* 1+Z >= 1 */
4314b5c65b8SLaurent Vivier             /* KISZERO */
4324b5c65b8SLaurent Vivier             fp0 = floatx80_sub(float32_to_floatx80(make_float32(0x3F800000),
4334b5c65b8SLaurent Vivier                                status), f, status); /* 1-F */
4344b5c65b8SLaurent Vivier             fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS Y-F = (1-F)+Z */
4354b5c65b8SLaurent Vivier             fp1 = packFloatx80(0, 0, 0); /* K = 0 */
4364b5c65b8SLaurent Vivier         } else {
4374b5c65b8SLaurent Vivier             /* KISNEG */
4384b5c65b8SLaurent Vivier             fp0 = floatx80_sub(float32_to_floatx80(make_float32(0x40000000),
4394b5c65b8SLaurent Vivier                                status), f, status); /* 2-F */
4404b5c65b8SLaurent Vivier             fp1 = floatx80_add(fp1, fp1, status); /* 2Z */
4414b5c65b8SLaurent Vivier             fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS Y-F = (2-F)+2Z */
4424b5c65b8SLaurent Vivier             fp1 = packFloatx80(1, one_exp, one_sig); /* K = -1 */
4434b5c65b8SLaurent Vivier         }
4444b5c65b8SLaurent Vivier         goto lp1cont1;
4454b5c65b8SLaurent Vivier     } else {
4464b5c65b8SLaurent Vivier         /* LP1ONE16 */
4474b5c65b8SLaurent Vivier         fp1 = floatx80_add(fp1, fp1, status); /* FP1 IS 2Z */
4484b5c65b8SLaurent Vivier         fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
4494b5c65b8SLaurent Vivier                            status), status); /* FP0 IS 1+X */
4504b5c65b8SLaurent Vivier 
4514b5c65b8SLaurent Vivier         /* LP1CONT2 */
4524b5c65b8SLaurent Vivier         fp1 = floatx80_div(fp1, fp0, status); /* U */
4534b5c65b8SLaurent Vivier         saveu = fp1;
4544b5c65b8SLaurent Vivier         fp0 = floatx80_mul(fp1, fp1, status); /* FP0 IS V = U*U */
4554b5c65b8SLaurent Vivier         fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS W = V*V */
4564b5c65b8SLaurent Vivier 
4574b5c65b8SLaurent Vivier         fp3 = float64_to_floatx80(make_float64(0x3F175496ADD7DAD6),
4584b5c65b8SLaurent Vivier                                   status); /* B5 */
4594b5c65b8SLaurent Vivier         fp2 = float64_to_floatx80(make_float64(0x3F3C71C2FE80C7E0),
4604b5c65b8SLaurent Vivier                                   status); /* B4 */
4614b5c65b8SLaurent Vivier         fp3 = floatx80_mul(fp3, fp1, status); /* W*B5 */
4624b5c65b8SLaurent Vivier         fp2 = floatx80_mul(fp2, fp1, status); /* W*B4 */
4634b5c65b8SLaurent Vivier         fp3 = floatx80_add(fp3, float64_to_floatx80(
4644b5c65b8SLaurent Vivier                            make_float64(0x3F624924928BCCFF), status),
4654b5c65b8SLaurent Vivier                            status); /* B3+W*B5 */
4664b5c65b8SLaurent Vivier         fp2 = floatx80_add(fp2, float64_to_floatx80(
4674b5c65b8SLaurent Vivier                            make_float64(0x3F899999999995EC), status),
4684b5c65b8SLaurent Vivier                            status); /* B2+W*B4 */
4694b5c65b8SLaurent Vivier         fp1 = floatx80_mul(fp1, fp3, status); /* W*(B3+W*B5) */
4704b5c65b8SLaurent Vivier         fp2 = floatx80_mul(fp2, fp0, status); /* V*(B2+W*B4) */
4714b5c65b8SLaurent Vivier         fp1 = floatx80_add(fp1, float64_to_floatx80(
4724b5c65b8SLaurent Vivier                            make_float64(0x3FB5555555555555), status),
4734b5c65b8SLaurent Vivier                            status); /* B1+W*(B3+W*B5) */
4744b5c65b8SLaurent Vivier 
4754b5c65b8SLaurent Vivier         fp0 = floatx80_mul(fp0, saveu, status); /* FP0 IS U*V */
4764b5c65b8SLaurent Vivier         fp1 = floatx80_add(fp1, fp2,
4774b5c65b8SLaurent Vivier                            status); /* B1+W*(B3+W*B5) + V*(B2+W*B4) */
4784b5c65b8SLaurent Vivier         fp0 = floatx80_mul(fp0, fp1,
4794b5c65b8SLaurent Vivier                            status); /* U*V*([B1+W*(B3+W*B5)] + [V*(B2+W*B4)]) */
4804b5c65b8SLaurent Vivier 
4814b5c65b8SLaurent Vivier         status->float_rounding_mode = user_rnd_mode;
4824b5c65b8SLaurent Vivier         status->floatx80_rounding_precision = user_rnd_prec;
4834b5c65b8SLaurent Vivier 
4844b5c65b8SLaurent Vivier         a = floatx80_add(fp0, saveu, status);
4854b5c65b8SLaurent Vivier 
4864b5c65b8SLaurent Vivier         /*if (!floatx80_is_zero(a)) { */
4874b5c65b8SLaurent Vivier             float_raise(float_flag_inexact, status);
4884b5c65b8SLaurent Vivier         /*} */
4894b5c65b8SLaurent Vivier 
4904b5c65b8SLaurent Vivier         return a;
4914b5c65b8SLaurent Vivier     }
4924b5c65b8SLaurent Vivier }
49350067bd1SLaurent Vivier 
49450067bd1SLaurent Vivier /*----------------------------------------------------------------------------
49550067bd1SLaurent Vivier  | Log base e
49650067bd1SLaurent Vivier  *----------------------------------------------------------------------------*/
49750067bd1SLaurent Vivier 
49850067bd1SLaurent Vivier floatx80 floatx80_logn(floatx80 a, float_status *status)
49950067bd1SLaurent Vivier {
50050067bd1SLaurent Vivier     flag aSign;
50150067bd1SLaurent Vivier     int32_t aExp;
50250067bd1SLaurent Vivier     uint64_t aSig, fSig;
50350067bd1SLaurent Vivier 
50450067bd1SLaurent Vivier     int8_t user_rnd_mode, user_rnd_prec;
50550067bd1SLaurent Vivier 
50650067bd1SLaurent Vivier     int32_t compact, j, k, adjk;
50750067bd1SLaurent Vivier     floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu;
50850067bd1SLaurent Vivier 
50950067bd1SLaurent Vivier     aSig = extractFloatx80Frac(a);
51050067bd1SLaurent Vivier     aExp = extractFloatx80Exp(a);
51150067bd1SLaurent Vivier     aSign = extractFloatx80Sign(a);
51250067bd1SLaurent Vivier 
51350067bd1SLaurent Vivier     if (aExp == 0x7FFF) {
51450067bd1SLaurent Vivier         if ((uint64_t) (aSig << 1)) {
51550067bd1SLaurent Vivier             propagateFloatx80NaNOneArg(a, status);
51650067bd1SLaurent Vivier         }
51750067bd1SLaurent Vivier         if (aSign == 0) {
51850067bd1SLaurent Vivier             return packFloatx80(0, floatx80_infinity.high,
51950067bd1SLaurent Vivier                                 floatx80_infinity.low);
52050067bd1SLaurent Vivier         }
52150067bd1SLaurent Vivier     }
52250067bd1SLaurent Vivier 
52350067bd1SLaurent Vivier     adjk = 0;
52450067bd1SLaurent Vivier 
52550067bd1SLaurent Vivier     if (aExp == 0) {
52650067bd1SLaurent Vivier         if (aSig == 0) { /* zero */
52750067bd1SLaurent Vivier             float_raise(float_flag_divbyzero, status);
52850067bd1SLaurent Vivier             return packFloatx80(1, floatx80_infinity.high,
52950067bd1SLaurent Vivier                                 floatx80_infinity.low);
53050067bd1SLaurent Vivier         }
53150067bd1SLaurent Vivier         if ((aSig & one_sig) == 0) { /* denormal */
53250067bd1SLaurent Vivier             normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
53350067bd1SLaurent Vivier             adjk = -100;
53450067bd1SLaurent Vivier             aExp += 100;
53550067bd1SLaurent Vivier             a = packFloatx80(aSign, aExp, aSig);
53650067bd1SLaurent Vivier         }
53750067bd1SLaurent Vivier     }
53850067bd1SLaurent Vivier 
53950067bd1SLaurent Vivier     if (aSign) {
54050067bd1SLaurent Vivier         float_raise(float_flag_invalid, status);
54150067bd1SLaurent Vivier         return floatx80_default_nan(status);
54250067bd1SLaurent Vivier     }
54350067bd1SLaurent Vivier 
54450067bd1SLaurent Vivier     user_rnd_mode = status->float_rounding_mode;
54550067bd1SLaurent Vivier     user_rnd_prec = status->floatx80_rounding_precision;
54650067bd1SLaurent Vivier     status->float_rounding_mode = float_round_nearest_even;
54750067bd1SLaurent Vivier     status->floatx80_rounding_precision = 80;
54850067bd1SLaurent Vivier 
54950067bd1SLaurent Vivier     compact = floatx80_make_compact(aExp, aSig);
55050067bd1SLaurent Vivier 
55150067bd1SLaurent Vivier     if (compact < 0x3FFEF07D || compact > 0x3FFF8841) {
55250067bd1SLaurent Vivier         /* |X| < 15/16 or |X| > 17/16 */
55350067bd1SLaurent Vivier         k = aExp - 0x3FFF;
55450067bd1SLaurent Vivier         k += adjk;
55550067bd1SLaurent Vivier         fp1 = int32_to_floatx80(k, status);
55650067bd1SLaurent Vivier 
55750067bd1SLaurent Vivier         fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000);
55850067bd1SLaurent Vivier         j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */
55950067bd1SLaurent Vivier 
56050067bd1SLaurent Vivier         f = packFloatx80(0, 0x3FFF, fSig); /* F */
56150067bd1SLaurent Vivier         fp0 = packFloatx80(0, 0x3FFF, aSig); /* Y */
56250067bd1SLaurent Vivier 
56350067bd1SLaurent Vivier         fp0 = floatx80_sub(fp0, f, status); /* Y-F */
56450067bd1SLaurent Vivier 
56550067bd1SLaurent Vivier         /* LP1CONT1 */
56650067bd1SLaurent Vivier         fp0 = floatx80_mul(fp0, log_tbl[j], status); /* FP0 IS U = (Y-F)/F */
56750067bd1SLaurent Vivier         logof2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC));
56850067bd1SLaurent Vivier         klog2 = floatx80_mul(fp1, logof2, status); /* FP1 IS K*LOG2 */
56950067bd1SLaurent Vivier         fp2 = floatx80_mul(fp0, fp0, status); /* FP2 IS V=U*U */
57050067bd1SLaurent Vivier 
57150067bd1SLaurent Vivier         fp3 = fp2;
57250067bd1SLaurent Vivier         fp1 = fp2;
57350067bd1SLaurent Vivier 
57450067bd1SLaurent Vivier         fp1 = floatx80_mul(fp1, float64_to_floatx80(
57550067bd1SLaurent Vivier                            make_float64(0x3FC2499AB5E4040B), status),
57650067bd1SLaurent Vivier                            status); /* V*A6 */
57750067bd1SLaurent Vivier         fp2 = floatx80_mul(fp2, float64_to_floatx80(
57850067bd1SLaurent Vivier                            make_float64(0xBFC555B5848CB7DB), status),
57950067bd1SLaurent Vivier                            status); /* V*A5 */
58050067bd1SLaurent Vivier         fp1 = floatx80_add(fp1, float64_to_floatx80(
58150067bd1SLaurent Vivier                            make_float64(0x3FC99999987D8730), status),
58250067bd1SLaurent Vivier                            status); /* A4+V*A6 */
58350067bd1SLaurent Vivier         fp2 = floatx80_add(fp2, float64_to_floatx80(
58450067bd1SLaurent Vivier                            make_float64(0xBFCFFFFFFF6F7E97), status),
58550067bd1SLaurent Vivier                            status); /* A3+V*A5 */
58650067bd1SLaurent Vivier         fp1 = floatx80_mul(fp1, fp3, status); /* V*(A4+V*A6) */
58750067bd1SLaurent Vivier         fp2 = floatx80_mul(fp2, fp3, status); /* V*(A3+V*A5) */
58850067bd1SLaurent Vivier         fp1 = floatx80_add(fp1, float64_to_floatx80(
58950067bd1SLaurent Vivier                            make_float64(0x3FD55555555555A4), status),
59050067bd1SLaurent Vivier                            status); /* A2+V*(A4+V*A6) */
59150067bd1SLaurent Vivier         fp2 = floatx80_add(fp2, float64_to_floatx80(
59250067bd1SLaurent Vivier                            make_float64(0xBFE0000000000008), status),
59350067bd1SLaurent Vivier                            status); /* A1+V*(A3+V*A5) */
59450067bd1SLaurent Vivier         fp1 = floatx80_mul(fp1, fp3, status); /* V*(A2+V*(A4+V*A6)) */
59550067bd1SLaurent Vivier         fp2 = floatx80_mul(fp2, fp3, status); /* V*(A1+V*(A3+V*A5)) */
59650067bd1SLaurent Vivier         fp1 = floatx80_mul(fp1, fp0, status); /* U*V*(A2+V*(A4+V*A6)) */
59750067bd1SLaurent Vivier         fp0 = floatx80_add(fp0, fp2, status); /* U+V*(A1+V*(A3+V*A5)) */
59850067bd1SLaurent Vivier 
59950067bd1SLaurent Vivier         fp1 = floatx80_add(fp1, log_tbl[j + 1],
60050067bd1SLaurent Vivier                            status); /* LOG(F)+U*V*(A2+V*(A4+V*A6)) */
60150067bd1SLaurent Vivier         fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS LOG(F) + LOG(1+U) */
60250067bd1SLaurent Vivier 
60350067bd1SLaurent Vivier         status->float_rounding_mode = user_rnd_mode;
60450067bd1SLaurent Vivier         status->floatx80_rounding_precision = user_rnd_prec;
60550067bd1SLaurent Vivier 
60650067bd1SLaurent Vivier         a = floatx80_add(fp0, klog2, status);
60750067bd1SLaurent Vivier 
60850067bd1SLaurent Vivier         float_raise(float_flag_inexact, status);
60950067bd1SLaurent Vivier 
61050067bd1SLaurent Vivier         return a;
61150067bd1SLaurent Vivier     } else { /* |X-1| >= 1/16 */
61250067bd1SLaurent Vivier         fp0 = a;
61350067bd1SLaurent Vivier         fp1 = a;
61450067bd1SLaurent Vivier         fp1 = floatx80_sub(fp1, float32_to_floatx80(make_float32(0x3F800000),
61550067bd1SLaurent Vivier                            status), status); /* FP1 IS X-1 */
61650067bd1SLaurent Vivier         fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
61750067bd1SLaurent Vivier                            status), status); /* FP0 IS X+1 */
61850067bd1SLaurent Vivier         fp1 = floatx80_add(fp1, fp1, status); /* FP1 IS 2(X-1) */
61950067bd1SLaurent Vivier 
62050067bd1SLaurent Vivier         /* LP1CONT2 */
62150067bd1SLaurent Vivier         fp1 = floatx80_div(fp1, fp0, status); /* U */
62250067bd1SLaurent Vivier         saveu = fp1;
62350067bd1SLaurent Vivier         fp0 = floatx80_mul(fp1, fp1, status); /* FP0 IS V = U*U */
62450067bd1SLaurent Vivier         fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS W = V*V */
62550067bd1SLaurent Vivier 
62650067bd1SLaurent Vivier         fp3 = float64_to_floatx80(make_float64(0x3F175496ADD7DAD6),
62750067bd1SLaurent Vivier                                   status); /* B5 */
62850067bd1SLaurent Vivier         fp2 = float64_to_floatx80(make_float64(0x3F3C71C2FE80C7E0),
62950067bd1SLaurent Vivier                                   status); /* B4 */
63050067bd1SLaurent Vivier         fp3 = floatx80_mul(fp3, fp1, status); /* W*B5 */
63150067bd1SLaurent Vivier         fp2 = floatx80_mul(fp2, fp1, status); /* W*B4 */
63250067bd1SLaurent Vivier         fp3 = floatx80_add(fp3, float64_to_floatx80(
63350067bd1SLaurent Vivier                            make_float64(0x3F624924928BCCFF), status),
63450067bd1SLaurent Vivier                            status); /* B3+W*B5 */
63550067bd1SLaurent Vivier         fp2 = floatx80_add(fp2, float64_to_floatx80(
63650067bd1SLaurent Vivier                            make_float64(0x3F899999999995EC), status),
63750067bd1SLaurent Vivier                            status); /* B2+W*B4 */
63850067bd1SLaurent Vivier         fp1 = floatx80_mul(fp1, fp3, status); /* W*(B3+W*B5) */
63950067bd1SLaurent Vivier         fp2 = floatx80_mul(fp2, fp0, status); /* V*(B2+W*B4) */
64050067bd1SLaurent Vivier         fp1 = floatx80_add(fp1, float64_to_floatx80(
64150067bd1SLaurent Vivier                            make_float64(0x3FB5555555555555), status),
64250067bd1SLaurent Vivier                            status); /* B1+W*(B3+W*B5) */
64350067bd1SLaurent Vivier 
64450067bd1SLaurent Vivier         fp0 = floatx80_mul(fp0, saveu, status); /* FP0 IS U*V */
64550067bd1SLaurent Vivier         fp1 = floatx80_add(fp1, fp2, status); /* B1+W*(B3+W*B5) + V*(B2+W*B4) */
64650067bd1SLaurent Vivier         fp0 = floatx80_mul(fp0, fp1,
64750067bd1SLaurent Vivier                            status); /* U*V*([B1+W*(B3+W*B5)] + [V*(B2+W*B4)]) */
64850067bd1SLaurent Vivier 
64950067bd1SLaurent Vivier         status->float_rounding_mode = user_rnd_mode;
65050067bd1SLaurent Vivier         status->floatx80_rounding_precision = user_rnd_prec;
65150067bd1SLaurent Vivier 
65250067bd1SLaurent Vivier         a = floatx80_add(fp0, saveu, status);
65350067bd1SLaurent Vivier 
65450067bd1SLaurent Vivier         /*if (!floatx80_is_zero(a)) { */
65550067bd1SLaurent Vivier             float_raise(float_flag_inexact, status);
65650067bd1SLaurent Vivier         /*} */
65750067bd1SLaurent Vivier 
65850067bd1SLaurent Vivier         return a;
65950067bd1SLaurent Vivier     }
66050067bd1SLaurent Vivier }
661248efb66SLaurent Vivier 
662248efb66SLaurent Vivier /*----------------------------------------------------------------------------
663248efb66SLaurent Vivier  | Log base 10
664248efb66SLaurent Vivier  *----------------------------------------------------------------------------*/
665248efb66SLaurent Vivier 
666248efb66SLaurent Vivier floatx80 floatx80_log10(floatx80 a, float_status *status)
667248efb66SLaurent Vivier {
668248efb66SLaurent Vivier     flag aSign;
669248efb66SLaurent Vivier     int32_t aExp;
670248efb66SLaurent Vivier     uint64_t aSig;
671248efb66SLaurent Vivier 
672248efb66SLaurent Vivier     int8_t user_rnd_mode, user_rnd_prec;
673248efb66SLaurent Vivier 
674248efb66SLaurent Vivier     floatx80 fp0, fp1;
675248efb66SLaurent Vivier 
676248efb66SLaurent Vivier     aSig = extractFloatx80Frac(a);
677248efb66SLaurent Vivier     aExp = extractFloatx80Exp(a);
678248efb66SLaurent Vivier     aSign = extractFloatx80Sign(a);
679248efb66SLaurent Vivier 
680248efb66SLaurent Vivier     if (aExp == 0x7FFF) {
681248efb66SLaurent Vivier         if ((uint64_t) (aSig << 1)) {
682248efb66SLaurent Vivier             propagateFloatx80NaNOneArg(a, status);
683248efb66SLaurent Vivier         }
684248efb66SLaurent Vivier         if (aSign == 0) {
685248efb66SLaurent Vivier             return packFloatx80(0, floatx80_infinity.high,
686248efb66SLaurent Vivier                                 floatx80_infinity.low);
687248efb66SLaurent Vivier         }
688248efb66SLaurent Vivier     }
689248efb66SLaurent Vivier 
690248efb66SLaurent Vivier     if (aExp == 0 && aSig == 0) {
691248efb66SLaurent Vivier         float_raise(float_flag_divbyzero, status);
692248efb66SLaurent Vivier         return packFloatx80(1, floatx80_infinity.high,
693248efb66SLaurent Vivier                             floatx80_infinity.low);
694248efb66SLaurent Vivier     }
695248efb66SLaurent Vivier 
696248efb66SLaurent Vivier     if (aSign) {
697248efb66SLaurent Vivier         float_raise(float_flag_invalid, status);
698248efb66SLaurent Vivier         return floatx80_default_nan(status);
699248efb66SLaurent Vivier     }
700248efb66SLaurent Vivier 
701248efb66SLaurent Vivier     user_rnd_mode = status->float_rounding_mode;
702248efb66SLaurent Vivier     user_rnd_prec = status->floatx80_rounding_precision;
703248efb66SLaurent Vivier     status->float_rounding_mode = float_round_nearest_even;
704248efb66SLaurent Vivier     status->floatx80_rounding_precision = 80;
705248efb66SLaurent Vivier 
706248efb66SLaurent Vivier     fp0 = floatx80_logn(a, status);
707248efb66SLaurent Vivier     fp1 = packFloatx80(0, 0x3FFD, LIT64(0xDE5BD8A937287195)); /* INV_L10 */
708248efb66SLaurent Vivier 
709248efb66SLaurent Vivier     status->float_rounding_mode = user_rnd_mode;
710248efb66SLaurent Vivier     status->floatx80_rounding_precision = user_rnd_prec;
711248efb66SLaurent Vivier 
712248efb66SLaurent Vivier     a = floatx80_mul(fp0, fp1, status); /* LOGN(X)*INV_L10 */
713248efb66SLaurent Vivier 
714248efb66SLaurent Vivier     float_raise(float_flag_inexact, status);
715248efb66SLaurent Vivier 
716248efb66SLaurent Vivier     return a;
717248efb66SLaurent Vivier }
71867b453edSLaurent Vivier 
71967b453edSLaurent Vivier /*----------------------------------------------------------------------------
72067b453edSLaurent Vivier  | Log base 2
72167b453edSLaurent Vivier  *----------------------------------------------------------------------------*/
72267b453edSLaurent Vivier 
72367b453edSLaurent Vivier floatx80 floatx80_log2(floatx80 a, float_status *status)
72467b453edSLaurent Vivier {
72567b453edSLaurent Vivier     flag aSign;
72667b453edSLaurent Vivier     int32_t aExp;
72767b453edSLaurent Vivier     uint64_t aSig;
72867b453edSLaurent Vivier 
72967b453edSLaurent Vivier     int8_t user_rnd_mode, user_rnd_prec;
73067b453edSLaurent Vivier 
73167b453edSLaurent Vivier     floatx80 fp0, fp1;
73267b453edSLaurent Vivier 
73367b453edSLaurent Vivier     aSig = extractFloatx80Frac(a);
73467b453edSLaurent Vivier     aExp = extractFloatx80Exp(a);
73567b453edSLaurent Vivier     aSign = extractFloatx80Sign(a);
73667b453edSLaurent Vivier 
73767b453edSLaurent Vivier     if (aExp == 0x7FFF) {
73867b453edSLaurent Vivier         if ((uint64_t) (aSig << 1)) {
73967b453edSLaurent Vivier             propagateFloatx80NaNOneArg(a, status);
74067b453edSLaurent Vivier         }
74167b453edSLaurent Vivier         if (aSign == 0) {
74267b453edSLaurent Vivier             return packFloatx80(0, floatx80_infinity.high,
74367b453edSLaurent Vivier                                 floatx80_infinity.low);
74467b453edSLaurent Vivier         }
74567b453edSLaurent Vivier     }
74667b453edSLaurent Vivier 
74767b453edSLaurent Vivier     if (aExp == 0) {
74867b453edSLaurent Vivier         if (aSig == 0) {
74967b453edSLaurent Vivier             float_raise(float_flag_divbyzero, status);
75067b453edSLaurent Vivier             return packFloatx80(1, floatx80_infinity.high,
75167b453edSLaurent Vivier                                 floatx80_infinity.low);
75267b453edSLaurent Vivier         }
75367b453edSLaurent Vivier         normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
75467b453edSLaurent Vivier     }
75567b453edSLaurent Vivier 
75667b453edSLaurent Vivier     if (aSign) {
75767b453edSLaurent Vivier         float_raise(float_flag_invalid, status);
75867b453edSLaurent Vivier         return floatx80_default_nan(status);
75967b453edSLaurent Vivier     }
76067b453edSLaurent Vivier 
76167b453edSLaurent Vivier     user_rnd_mode = status->float_rounding_mode;
76267b453edSLaurent Vivier     user_rnd_prec = status->floatx80_rounding_precision;
76367b453edSLaurent Vivier     status->float_rounding_mode = float_round_nearest_even;
76467b453edSLaurent Vivier     status->floatx80_rounding_precision = 80;
76567b453edSLaurent Vivier 
76667b453edSLaurent Vivier     if (aSig == one_sig) { /* X is 2^k */
76767b453edSLaurent Vivier         status->float_rounding_mode = user_rnd_mode;
76867b453edSLaurent Vivier         status->floatx80_rounding_precision = user_rnd_prec;
76967b453edSLaurent Vivier 
77067b453edSLaurent Vivier         a = int32_to_floatx80(aExp - 0x3FFF, status);
77167b453edSLaurent Vivier     } else {
77267b453edSLaurent Vivier         fp0 = floatx80_logn(a, status);
77367b453edSLaurent Vivier         fp1 = packFloatx80(0, 0x3FFF, LIT64(0xB8AA3B295C17F0BC)); /* INV_L2 */
77467b453edSLaurent Vivier 
77567b453edSLaurent Vivier         status->float_rounding_mode = user_rnd_mode;
77667b453edSLaurent Vivier         status->floatx80_rounding_precision = user_rnd_prec;
77767b453edSLaurent Vivier 
77867b453edSLaurent Vivier         a = floatx80_mul(fp0, fp1, status); /* LOGN(X)*INV_L2 */
77967b453edSLaurent Vivier     }
78067b453edSLaurent Vivier 
78167b453edSLaurent Vivier     float_raise(float_flag_inexact, status);
78267b453edSLaurent Vivier 
78367b453edSLaurent Vivier     return a;
78467b453edSLaurent Vivier }
78540ad0873SLaurent Vivier 
78640ad0873SLaurent Vivier /*----------------------------------------------------------------------------
78740ad0873SLaurent Vivier  | e to x
78840ad0873SLaurent Vivier  *----------------------------------------------------------------------------*/
78940ad0873SLaurent Vivier 
79040ad0873SLaurent Vivier floatx80 floatx80_etox(floatx80 a, float_status *status)
79140ad0873SLaurent Vivier {
79240ad0873SLaurent Vivier     flag aSign;
79340ad0873SLaurent Vivier     int32_t aExp;
79440ad0873SLaurent Vivier     uint64_t aSig;
79540ad0873SLaurent Vivier 
79640ad0873SLaurent Vivier     int8_t user_rnd_mode, user_rnd_prec;
79740ad0873SLaurent Vivier 
79840ad0873SLaurent Vivier     int32_t compact, n, j, k, m, m1;
79940ad0873SLaurent Vivier     floatx80 fp0, fp1, fp2, fp3, l2, scale, adjscale;
80040ad0873SLaurent Vivier     flag adjflag;
80140ad0873SLaurent Vivier 
80240ad0873SLaurent Vivier     aSig = extractFloatx80Frac(a);
80340ad0873SLaurent Vivier     aExp = extractFloatx80Exp(a);
80440ad0873SLaurent Vivier     aSign = extractFloatx80Sign(a);
80540ad0873SLaurent Vivier 
80640ad0873SLaurent Vivier     if (aExp == 0x7FFF) {
80740ad0873SLaurent Vivier         if ((uint64_t) (aSig << 1)) {
80840ad0873SLaurent Vivier             return propagateFloatx80NaNOneArg(a, status);
80940ad0873SLaurent Vivier         }
81040ad0873SLaurent Vivier         if (aSign) {
81140ad0873SLaurent Vivier             return packFloatx80(0, 0, 0);
81240ad0873SLaurent Vivier         }
81340ad0873SLaurent Vivier         return packFloatx80(0, floatx80_infinity.high,
81440ad0873SLaurent Vivier                             floatx80_infinity.low);
81540ad0873SLaurent Vivier     }
81640ad0873SLaurent Vivier 
81740ad0873SLaurent Vivier     if (aExp == 0 && aSig == 0) {
81840ad0873SLaurent Vivier         return packFloatx80(0, one_exp, one_sig);
81940ad0873SLaurent Vivier     }
82040ad0873SLaurent Vivier 
82140ad0873SLaurent Vivier     user_rnd_mode = status->float_rounding_mode;
82240ad0873SLaurent Vivier     user_rnd_prec = status->floatx80_rounding_precision;
82340ad0873SLaurent Vivier     status->float_rounding_mode = float_round_nearest_even;
82440ad0873SLaurent Vivier     status->floatx80_rounding_precision = 80;
82540ad0873SLaurent Vivier 
82640ad0873SLaurent Vivier     adjflag = 0;
82740ad0873SLaurent Vivier 
82840ad0873SLaurent Vivier     if (aExp >= 0x3FBE) { /* |X| >= 2^(-65) */
82940ad0873SLaurent Vivier         compact = floatx80_make_compact(aExp, aSig);
83040ad0873SLaurent Vivier 
83140ad0873SLaurent Vivier         if (compact < 0x400CB167) { /* |X| < 16380 log2 */
83240ad0873SLaurent Vivier             fp0 = a;
83340ad0873SLaurent Vivier             fp1 = a;
83440ad0873SLaurent Vivier             fp0 = floatx80_mul(fp0, float32_to_floatx80(
83540ad0873SLaurent Vivier                                make_float32(0x42B8AA3B), status),
83640ad0873SLaurent Vivier                                status); /* 64/log2 * X */
83740ad0873SLaurent Vivier             adjflag = 0;
83840ad0873SLaurent Vivier             n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */
83940ad0873SLaurent Vivier             fp0 = int32_to_floatx80(n, status);
84040ad0873SLaurent Vivier 
84140ad0873SLaurent Vivier             j = n & 0x3F; /* J = N mod 64 */
84240ad0873SLaurent Vivier             m = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
84340ad0873SLaurent Vivier             if (n < 0 && j) {
84440ad0873SLaurent Vivier                 /* arithmetic right shift is division and
84540ad0873SLaurent Vivier                  * round towards minus infinity
84640ad0873SLaurent Vivier                  */
84740ad0873SLaurent Vivier                 m--;
84840ad0873SLaurent Vivier             }
84940ad0873SLaurent Vivier             m += 0x3FFF; /* biased exponent of 2^(M) */
85040ad0873SLaurent Vivier 
85140ad0873SLaurent Vivier         expcont1:
85240ad0873SLaurent Vivier             fp2 = fp0; /* N */
85340ad0873SLaurent Vivier             fp0 = floatx80_mul(fp0, float32_to_floatx80(
85440ad0873SLaurent Vivier                                make_float32(0xBC317218), status),
85540ad0873SLaurent Vivier                                status); /* N * L1, L1 = lead(-log2/64) */
85640ad0873SLaurent Vivier             l2 = packFloatx80(0, 0x3FDC, LIT64(0x82E308654361C4C6));
85740ad0873SLaurent Vivier             fp2 = floatx80_mul(fp2, l2, status); /* N * L2, L1+L2 = -log2/64 */
85840ad0873SLaurent Vivier             fp0 = floatx80_add(fp0, fp1, status); /* X + N*L1 */
85940ad0873SLaurent Vivier             fp0 = floatx80_add(fp0, fp2, status); /* R */
86040ad0873SLaurent Vivier 
86140ad0873SLaurent Vivier             fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
86240ad0873SLaurent Vivier             fp2 = float32_to_floatx80(make_float32(0x3AB60B70),
86340ad0873SLaurent Vivier                                       status); /* A5 */
86440ad0873SLaurent Vivier             fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*A5 */
86540ad0873SLaurent Vivier             fp3 = floatx80_mul(float32_to_floatx80(make_float32(0x3C088895),
86640ad0873SLaurent Vivier                                status), fp1,
86740ad0873SLaurent Vivier                                status); /* fp3 is S*A4 */
86840ad0873SLaurent Vivier             fp2 = floatx80_add(fp2, float64_to_floatx80(make_float64(
86940ad0873SLaurent Vivier                                0x3FA5555555554431), status),
87040ad0873SLaurent Vivier                                status); /* fp2 is A3+S*A5 */
87140ad0873SLaurent Vivier             fp3 = floatx80_add(fp3, float64_to_floatx80(make_float64(
87240ad0873SLaurent Vivier                                0x3FC5555555554018), status),
87340ad0873SLaurent Vivier                                status); /* fp3 is A2+S*A4 */
87440ad0873SLaurent Vivier             fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*(A3+S*A5) */
87540ad0873SLaurent Vivier             fp3 = floatx80_mul(fp3, fp1, status); /* fp3 is S*(A2+S*A4) */
87640ad0873SLaurent Vivier             fp2 = floatx80_add(fp2, float32_to_floatx80(
87740ad0873SLaurent Vivier                                make_float32(0x3F000000), status),
87840ad0873SLaurent Vivier                                status); /* fp2 is A1+S*(A3+S*A5) */
87940ad0873SLaurent Vivier             fp3 = floatx80_mul(fp3, fp0, status); /* fp3 IS R*S*(A2+S*A4) */
88040ad0873SLaurent Vivier             fp2 = floatx80_mul(fp2, fp1,
88140ad0873SLaurent Vivier                                status); /* fp2 IS S*(A1+S*(A3+S*A5)) */
88240ad0873SLaurent Vivier             fp0 = floatx80_add(fp0, fp3, status); /* fp0 IS R+R*S*(A2+S*A4) */
88340ad0873SLaurent Vivier             fp0 = floatx80_add(fp0, fp2, status); /* fp0 IS EXP(R) - 1 */
88440ad0873SLaurent Vivier 
88540ad0873SLaurent Vivier             fp1 = exp_tbl[j];
88640ad0873SLaurent Vivier             fp0 = floatx80_mul(fp0, fp1, status); /* 2^(J/64)*(Exp(R)-1) */
88740ad0873SLaurent Vivier             fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], status),
88840ad0873SLaurent Vivier                                status); /* accurate 2^(J/64) */
88940ad0873SLaurent Vivier             fp0 = floatx80_add(fp0, fp1,
89040ad0873SLaurent Vivier                                status); /* 2^(J/64) + 2^(J/64)*(Exp(R)-1) */
89140ad0873SLaurent Vivier 
89240ad0873SLaurent Vivier             scale = packFloatx80(0, m, one_sig);
89340ad0873SLaurent Vivier             if (adjflag) {
89440ad0873SLaurent Vivier                 adjscale = packFloatx80(0, m1, one_sig);
89540ad0873SLaurent Vivier                 fp0 = floatx80_mul(fp0, adjscale, status);
89640ad0873SLaurent Vivier             }
89740ad0873SLaurent Vivier 
89840ad0873SLaurent Vivier             status->float_rounding_mode = user_rnd_mode;
89940ad0873SLaurent Vivier             status->floatx80_rounding_precision = user_rnd_prec;
90040ad0873SLaurent Vivier 
90140ad0873SLaurent Vivier             a = floatx80_mul(fp0, scale, status);
90240ad0873SLaurent Vivier 
90340ad0873SLaurent Vivier             float_raise(float_flag_inexact, status);
90440ad0873SLaurent Vivier 
90540ad0873SLaurent Vivier             return a;
90640ad0873SLaurent Vivier         } else { /* |X| >= 16380 log2 */
90740ad0873SLaurent Vivier             if (compact > 0x400CB27C) { /* |X| >= 16480 log2 */
90840ad0873SLaurent Vivier                 status->float_rounding_mode = user_rnd_mode;
90940ad0873SLaurent Vivier                 status->floatx80_rounding_precision = user_rnd_prec;
91040ad0873SLaurent Vivier                 if (aSign) {
91140ad0873SLaurent Vivier                     a = roundAndPackFloatx80(
91240ad0873SLaurent Vivier                                            status->floatx80_rounding_precision,
91340ad0873SLaurent Vivier                                            0, -0x1000, aSig, 0, status);
91440ad0873SLaurent Vivier                 } else {
91540ad0873SLaurent Vivier                     a = roundAndPackFloatx80(
91640ad0873SLaurent Vivier                                            status->floatx80_rounding_precision,
91740ad0873SLaurent Vivier                                            0, 0x8000, aSig, 0, status);
91840ad0873SLaurent Vivier                 }
91940ad0873SLaurent Vivier                 float_raise(float_flag_inexact, status);
92040ad0873SLaurent Vivier 
92140ad0873SLaurent Vivier                 return a;
92240ad0873SLaurent Vivier             } else {
92340ad0873SLaurent Vivier                 fp0 = a;
92440ad0873SLaurent Vivier                 fp1 = a;
92540ad0873SLaurent Vivier                 fp0 = floatx80_mul(fp0, float32_to_floatx80(
92640ad0873SLaurent Vivier                                    make_float32(0x42B8AA3B), status),
92740ad0873SLaurent Vivier                                    status); /* 64/log2 * X */
92840ad0873SLaurent Vivier                 adjflag = 1;
92940ad0873SLaurent Vivier                 n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */
93040ad0873SLaurent Vivier                 fp0 = int32_to_floatx80(n, status);
93140ad0873SLaurent Vivier 
93240ad0873SLaurent Vivier                 j = n & 0x3F; /* J = N mod 64 */
93340ad0873SLaurent Vivier                 /* NOTE: this is really arithmetic right shift by 6 */
93440ad0873SLaurent Vivier                 k = n / 64;
93540ad0873SLaurent Vivier                 if (n < 0 && j) {
93640ad0873SLaurent Vivier                     /* arithmetic right shift is division and
93740ad0873SLaurent Vivier                      * round towards minus infinity
93840ad0873SLaurent Vivier                      */
93940ad0873SLaurent Vivier                     k--;
94040ad0873SLaurent Vivier                 }
94140ad0873SLaurent Vivier                 /* NOTE: this is really arithmetic right shift by 1 */
94240ad0873SLaurent Vivier                 m1 = k / 2;
94340ad0873SLaurent Vivier                 if (k < 0 && (k & 1)) {
94440ad0873SLaurent Vivier                     /* arithmetic right shift is division and
94540ad0873SLaurent Vivier                      * round towards minus infinity
94640ad0873SLaurent Vivier                      */
94740ad0873SLaurent Vivier                     m1--;
94840ad0873SLaurent Vivier                 }
94940ad0873SLaurent Vivier                 m = k - m1;
95040ad0873SLaurent Vivier                 m1 += 0x3FFF; /* biased exponent of 2^(M1) */
95140ad0873SLaurent Vivier                 m += 0x3FFF; /* biased exponent of 2^(M) */
95240ad0873SLaurent Vivier 
95340ad0873SLaurent Vivier                 goto expcont1;
95440ad0873SLaurent Vivier             }
95540ad0873SLaurent Vivier         }
95640ad0873SLaurent Vivier     } else { /* |X| < 2^(-65) */
95740ad0873SLaurent Vivier         status->float_rounding_mode = user_rnd_mode;
95840ad0873SLaurent Vivier         status->floatx80_rounding_precision = user_rnd_prec;
95940ad0873SLaurent Vivier 
96040ad0873SLaurent Vivier         a = floatx80_add(a, float32_to_floatx80(make_float32(0x3F800000),
96140ad0873SLaurent Vivier                          status), status); /* 1 + X */
96240ad0873SLaurent Vivier 
96340ad0873SLaurent Vivier         float_raise(float_flag_inexact, status);
96440ad0873SLaurent Vivier 
96540ad0873SLaurent Vivier         return a;
96640ad0873SLaurent Vivier     }
96740ad0873SLaurent Vivier }
968*068f1615SLaurent Vivier 
969*068f1615SLaurent Vivier /*----------------------------------------------------------------------------
970*068f1615SLaurent Vivier  | 2 to x
971*068f1615SLaurent Vivier  *----------------------------------------------------------------------------*/
972*068f1615SLaurent Vivier 
973*068f1615SLaurent Vivier floatx80 floatx80_twotox(floatx80 a, float_status *status)
974*068f1615SLaurent Vivier {
975*068f1615SLaurent Vivier     flag aSign;
976*068f1615SLaurent Vivier     int32_t aExp;
977*068f1615SLaurent Vivier     uint64_t aSig;
978*068f1615SLaurent Vivier 
979*068f1615SLaurent Vivier     int8_t user_rnd_mode, user_rnd_prec;
980*068f1615SLaurent Vivier 
981*068f1615SLaurent Vivier     int32_t compact, n, j, l, m, m1;
982*068f1615SLaurent Vivier     floatx80 fp0, fp1, fp2, fp3, adjfact, fact1, fact2;
983*068f1615SLaurent Vivier 
984*068f1615SLaurent Vivier     aSig = extractFloatx80Frac(a);
985*068f1615SLaurent Vivier     aExp = extractFloatx80Exp(a);
986*068f1615SLaurent Vivier     aSign = extractFloatx80Sign(a);
987*068f1615SLaurent Vivier 
988*068f1615SLaurent Vivier     if (aExp == 0x7FFF) {
989*068f1615SLaurent Vivier         if ((uint64_t) (aSig << 1)) {
990*068f1615SLaurent Vivier             return propagateFloatx80NaNOneArg(a, status);
991*068f1615SLaurent Vivier         }
992*068f1615SLaurent Vivier         if (aSign) {
993*068f1615SLaurent Vivier             return packFloatx80(0, 0, 0);
994*068f1615SLaurent Vivier         }
995*068f1615SLaurent Vivier         return packFloatx80(0, floatx80_infinity.high,
996*068f1615SLaurent Vivier                             floatx80_infinity.low);
997*068f1615SLaurent Vivier     }
998*068f1615SLaurent Vivier 
999*068f1615SLaurent Vivier     if (aExp == 0 && aSig == 0) {
1000*068f1615SLaurent Vivier         return packFloatx80(0, one_exp, one_sig);
1001*068f1615SLaurent Vivier     }
1002*068f1615SLaurent Vivier 
1003*068f1615SLaurent Vivier     user_rnd_mode = status->float_rounding_mode;
1004*068f1615SLaurent Vivier     user_rnd_prec = status->floatx80_rounding_precision;
1005*068f1615SLaurent Vivier     status->float_rounding_mode = float_round_nearest_even;
1006*068f1615SLaurent Vivier     status->floatx80_rounding_precision = 80;
1007*068f1615SLaurent Vivier 
1008*068f1615SLaurent Vivier     fp0 = a;
1009*068f1615SLaurent Vivier 
1010*068f1615SLaurent Vivier     compact = floatx80_make_compact(aExp, aSig);
1011*068f1615SLaurent Vivier 
1012*068f1615SLaurent Vivier     if (compact < 0x3FB98000 || compact > 0x400D80C0) {
1013*068f1615SLaurent Vivier         /* |X| > 16480 or |X| < 2^(-70) */
1014*068f1615SLaurent Vivier         if (compact > 0x3FFF8000) { /* |X| > 16480 */
1015*068f1615SLaurent Vivier             status->float_rounding_mode = user_rnd_mode;
1016*068f1615SLaurent Vivier             status->floatx80_rounding_precision = user_rnd_prec;
1017*068f1615SLaurent Vivier 
1018*068f1615SLaurent Vivier             if (aSign) {
1019*068f1615SLaurent Vivier                 return roundAndPackFloatx80(status->floatx80_rounding_precision,
1020*068f1615SLaurent Vivier                                             0, -0x1000, aSig, 0, status);
1021*068f1615SLaurent Vivier             } else {
1022*068f1615SLaurent Vivier                 return roundAndPackFloatx80(status->floatx80_rounding_precision,
1023*068f1615SLaurent Vivier                                             0, 0x8000, aSig, 0, status);
1024*068f1615SLaurent Vivier             }
1025*068f1615SLaurent Vivier         } else { /* |X| < 2^(-70) */
1026*068f1615SLaurent Vivier             status->float_rounding_mode = user_rnd_mode;
1027*068f1615SLaurent Vivier             status->floatx80_rounding_precision = user_rnd_prec;
1028*068f1615SLaurent Vivier 
1029*068f1615SLaurent Vivier             a = floatx80_add(fp0, float32_to_floatx80(
1030*068f1615SLaurent Vivier                              make_float32(0x3F800000), status),
1031*068f1615SLaurent Vivier                              status); /* 1 + X */
1032*068f1615SLaurent Vivier 
1033*068f1615SLaurent Vivier             float_raise(float_flag_inexact, status);
1034*068f1615SLaurent Vivier 
1035*068f1615SLaurent Vivier             return a;
1036*068f1615SLaurent Vivier         }
1037*068f1615SLaurent Vivier     } else { /* 2^(-70) <= |X| <= 16480 */
1038*068f1615SLaurent Vivier         fp1 = fp0; /* X */
1039*068f1615SLaurent Vivier         fp1 = floatx80_mul(fp1, float32_to_floatx80(
1040*068f1615SLaurent Vivier                            make_float32(0x42800000), status),
1041*068f1615SLaurent Vivier                            status); /* X * 64 */
1042*068f1615SLaurent Vivier         n = floatx80_to_int32(fp1, status);
1043*068f1615SLaurent Vivier         fp1 = int32_to_floatx80(n, status);
1044*068f1615SLaurent Vivier         j = n & 0x3F;
1045*068f1615SLaurent Vivier         l = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
1046*068f1615SLaurent Vivier         if (n < 0 && j) {
1047*068f1615SLaurent Vivier             /* arithmetic right shift is division and
1048*068f1615SLaurent Vivier              * round towards minus infinity
1049*068f1615SLaurent Vivier              */
1050*068f1615SLaurent Vivier             l--;
1051*068f1615SLaurent Vivier         }
1052*068f1615SLaurent Vivier         m = l / 2; /* NOTE: this is really arithmetic right shift by 1 */
1053*068f1615SLaurent Vivier         if (l < 0 && (l & 1)) {
1054*068f1615SLaurent Vivier             /* arithmetic right shift is division and
1055*068f1615SLaurent Vivier              * round towards minus infinity
1056*068f1615SLaurent Vivier              */
1057*068f1615SLaurent Vivier             m--;
1058*068f1615SLaurent Vivier         }
1059*068f1615SLaurent Vivier         m1 = l - m;
1060*068f1615SLaurent Vivier         m1 += 0x3FFF; /* ADJFACT IS 2^(M') */
1061*068f1615SLaurent Vivier 
1062*068f1615SLaurent Vivier         adjfact = packFloatx80(0, m1, one_sig);
1063*068f1615SLaurent Vivier         fact1 = exp2_tbl[j];
1064*068f1615SLaurent Vivier         fact1.high += m;
1065*068f1615SLaurent Vivier         fact2.high = exp2_tbl2[j] >> 16;
1066*068f1615SLaurent Vivier         fact2.high += m;
1067*068f1615SLaurent Vivier         fact2.low = (uint64_t)(exp2_tbl2[j] & 0xFFFF);
1068*068f1615SLaurent Vivier         fact2.low <<= 48;
1069*068f1615SLaurent Vivier 
1070*068f1615SLaurent Vivier         fp1 = floatx80_mul(fp1, float32_to_floatx80(
1071*068f1615SLaurent Vivier                            make_float32(0x3C800000), status),
1072*068f1615SLaurent Vivier                            status); /* (1/64)*N */
1073*068f1615SLaurent Vivier         fp0 = floatx80_sub(fp0, fp1, status); /* X - (1/64)*INT(64 X) */
1074*068f1615SLaurent Vivier         fp2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC)); /* LOG2 */
1075*068f1615SLaurent Vivier         fp0 = floatx80_mul(fp0, fp2, status); /* R */
1076*068f1615SLaurent Vivier 
1077*068f1615SLaurent Vivier         /* EXPR */
1078*068f1615SLaurent Vivier         fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
1079*068f1615SLaurent Vivier         fp2 = float64_to_floatx80(make_float64(0x3F56C16D6F7BD0B2),
1080*068f1615SLaurent Vivier                                   status); /* A5 */
1081*068f1615SLaurent Vivier         fp3 = float64_to_floatx80(make_float64(0x3F811112302C712C),
1082*068f1615SLaurent Vivier                                   status); /* A4 */
1083*068f1615SLaurent Vivier         fp2 = floatx80_mul(fp2, fp1, status); /* S*A5 */
1084*068f1615SLaurent Vivier         fp3 = floatx80_mul(fp3, fp1, status); /* S*A4 */
1085*068f1615SLaurent Vivier         fp2 = floatx80_add(fp2, float64_to_floatx80(
1086*068f1615SLaurent Vivier                            make_float64(0x3FA5555555554CC1), status),
1087*068f1615SLaurent Vivier                            status); /* A3+S*A5 */
1088*068f1615SLaurent Vivier         fp3 = floatx80_add(fp3, float64_to_floatx80(
1089*068f1615SLaurent Vivier                            make_float64(0x3FC5555555554A54), status),
1090*068f1615SLaurent Vivier                            status); /* A2+S*A4 */
1091*068f1615SLaurent Vivier         fp2 = floatx80_mul(fp2, fp1, status); /* S*(A3+S*A5) */
1092*068f1615SLaurent Vivier         fp3 = floatx80_mul(fp3, fp1, status); /* S*(A2+S*A4) */
1093*068f1615SLaurent Vivier         fp2 = floatx80_add(fp2, float64_to_floatx80(
1094*068f1615SLaurent Vivier                            make_float64(0x3FE0000000000000), status),
1095*068f1615SLaurent Vivier                            status); /* A1+S*(A3+S*A5) */
1096*068f1615SLaurent Vivier         fp3 = floatx80_mul(fp3, fp0, status); /* R*S*(A2+S*A4) */
1097*068f1615SLaurent Vivier 
1098*068f1615SLaurent Vivier         fp2 = floatx80_mul(fp2, fp1, status); /* S*(A1+S*(A3+S*A5)) */
1099*068f1615SLaurent Vivier         fp0 = floatx80_add(fp0, fp3, status); /* R+R*S*(A2+S*A4) */
1100*068f1615SLaurent Vivier         fp0 = floatx80_add(fp0, fp2, status); /* EXP(R) - 1 */
1101*068f1615SLaurent Vivier 
1102*068f1615SLaurent Vivier         fp0 = floatx80_mul(fp0, fact1, status);
1103*068f1615SLaurent Vivier         fp0 = floatx80_add(fp0, fact2, status);
1104*068f1615SLaurent Vivier         fp0 = floatx80_add(fp0, fact1, status);
1105*068f1615SLaurent Vivier 
1106*068f1615SLaurent Vivier         status->float_rounding_mode = user_rnd_mode;
1107*068f1615SLaurent Vivier         status->floatx80_rounding_precision = user_rnd_prec;
1108*068f1615SLaurent Vivier 
1109*068f1615SLaurent Vivier         a = floatx80_mul(fp0, adjfact, status);
1110*068f1615SLaurent Vivier 
1111*068f1615SLaurent Vivier         float_raise(float_flag_inexact, status);
1112*068f1615SLaurent Vivier 
1113*068f1615SLaurent Vivier         return a;
1114*068f1615SLaurent Vivier     }
1115*068f1615SLaurent Vivier }
1116