19888Sandreas@sandberg.pp.se/*
29888Sandreas@sandberg.pp.se * Copyright (c) 2013, Andreas Sandberg
39888Sandreas@sandberg.pp.se * All rights reserved.
49888Sandreas@sandberg.pp.se *
59888Sandreas@sandberg.pp.se * Redistribution and use in source and binary forms, with or without
69888Sandreas@sandberg.pp.se * modification, are permitted provided that the following conditions
79888Sandreas@sandberg.pp.se * are met:
89888Sandreas@sandberg.pp.se *
99888Sandreas@sandberg.pp.se * 1. Redistributions of source code must retain the above copyright
109888Sandreas@sandberg.pp.se *    notice, this list of conditions and the following disclaimer.
119888Sandreas@sandberg.pp.se * 2. Redistributions in binary form must reproduce the above
129888Sandreas@sandberg.pp.se *    copyright notice, this list of conditions and the following
139888Sandreas@sandberg.pp.se *    disclaimer in the documentation and/or other materials provided
149888Sandreas@sandberg.pp.se *    with the distribution.
159888Sandreas@sandberg.pp.se *
169888Sandreas@sandberg.pp.se * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
179888Sandreas@sandberg.pp.se * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
189888Sandreas@sandberg.pp.se * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
199888Sandreas@sandberg.pp.se * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
209888Sandreas@sandberg.pp.se * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
219888Sandreas@sandberg.pp.se * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
229888Sandreas@sandberg.pp.se * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
239888Sandreas@sandberg.pp.se * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
249888Sandreas@sandberg.pp.se * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
259888Sandreas@sandberg.pp.se * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
269888Sandreas@sandberg.pp.se * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
279888Sandreas@sandberg.pp.se * OF THE POSSIBILITY OF SUCH DAMAGE.
289888Sandreas@sandberg.pp.se */
299888Sandreas@sandberg.pp.se
309888Sandreas@sandberg.pp.se#include <fputils/fp80.h>
3110480SAndreas.Sandberg@ARM.com#include <fputils/fp64.h>
329888Sandreas@sandberg.pp.se#include "fpbits.h"
339888Sandreas@sandberg.pp.se
349888Sandreas@sandberg.pp.se#include <assert.h>
359888Sandreas@sandberg.pp.se#include <stdint.h>
369888Sandreas@sandberg.pp.se
379888Sandreas@sandberg.pp.se#include <stdio.h>
389888Sandreas@sandberg.pp.se
399888Sandreas@sandberg.pp.se
4010480SAndreas.Sandberg@ARM.comconst fp80_t fp80_pinf  = BUILD_FP80(0, 0,               FP80_EXP_SPECIAL);
4110480SAndreas.Sandberg@ARM.comconst fp80_t fp80_ninf  = BUILD_FP80(1, 0,               FP80_EXP_SPECIAL);
4210480SAndreas.Sandberg@ARM.comconst fp80_t fp80_qnan  = BUILD_FP80(0, FP80_FRAC_QNAN,  FP80_EXP_SPECIAL);
439888Sandreas@sandberg.pp.seconst fp80_t fp80_qnani = BUILD_FP80(1, FP80_FRAC_QNANI, FP80_EXP_SPECIAL);
4410480SAndreas.Sandberg@ARM.comconst fp80_t fp80_snan  = BUILD_FP80(0, FP80_FRAC_SNAN,  FP80_EXP_SPECIAL);
4510480SAndreas.Sandberg@ARM.comconst fp80_t fp80_nan   = BUILD_FP80(0, FP80_FRAC_QNAN,  FP80_EXP_SPECIAL);
469888Sandreas@sandberg.pp.se
479888Sandreas@sandberg.pp.seint
489888Sandreas@sandberg.pp.sefp80_sgn(fp80_t fp80)
499888Sandreas@sandberg.pp.se{
509899Sandreas@sandberg.pp.se    return (fp80.repr.se & FP80_SIGN_BIT) ? -1 : 1;
519888Sandreas@sandberg.pp.se}
529888Sandreas@sandberg.pp.se
539888Sandreas@sandberg.pp.seint
549888Sandreas@sandberg.pp.sefp80_isspecial(fp80_t fp80)
559888Sandreas@sandberg.pp.se{
569888Sandreas@sandberg.pp.se    const int exp = FP80_EXP(fp80);
579888Sandreas@sandberg.pp.se
589888Sandreas@sandberg.pp.se    return exp == FP80_EXP_SPECIAL;
599888Sandreas@sandberg.pp.se}
609888Sandreas@sandberg.pp.se
619888Sandreas@sandberg.pp.seint
629888Sandreas@sandberg.pp.sefp80_isinf(fp80_t fp80)
639888Sandreas@sandberg.pp.se{
649888Sandreas@sandberg.pp.se    const uint64_t frac = FP80_FRAC(fp80);
659888Sandreas@sandberg.pp.se
669888Sandreas@sandberg.pp.se    return fp80_isspecial(fp80) && frac == 0 ? fp80_sgn(fp80) : 0;
679888Sandreas@sandberg.pp.se}
689888Sandreas@sandberg.pp.se
699888Sandreas@sandberg.pp.se
709888Sandreas@sandberg.pp.seint
719888Sandreas@sandberg.pp.sefp80_isqnan(fp80_t fp80)
729888Sandreas@sandberg.pp.se{
739888Sandreas@sandberg.pp.se    const uint64_t frac = FP80_FRAC(fp80);
749888Sandreas@sandberg.pp.se
759888Sandreas@sandberg.pp.se    return fp80_isspecial(fp80) && (frac & FP80_QNAN_BIT);
769888Sandreas@sandberg.pp.se}
779888Sandreas@sandberg.pp.se
789888Sandreas@sandberg.pp.seint
799888Sandreas@sandberg.pp.sefp80_isqnani(fp80_t fp80)
809888Sandreas@sandberg.pp.se{
819899Sandreas@sandberg.pp.se    const uint64_t frac_low = fp80.repr.fi & (FP80_FRAC_MASK >> 1);
829888Sandreas@sandberg.pp.se
839899Sandreas@sandberg.pp.se    return fp80_isqnan(fp80) && (fp80.repr.se & FP80_SIGN_BIT) && !frac_low;
849888Sandreas@sandberg.pp.se}
859888Sandreas@sandberg.pp.se
869888Sandreas@sandberg.pp.seint
879888Sandreas@sandberg.pp.sefp80_issnan(fp80_t fp80)
889888Sandreas@sandberg.pp.se{
899888Sandreas@sandberg.pp.se    const uint64_t frac = FP80_FRAC(fp80);
909888Sandreas@sandberg.pp.se
919888Sandreas@sandberg.pp.se    return fp80_isspecial(fp80) && !(frac & FP80_QNAN_BIT) && frac;
929888Sandreas@sandberg.pp.se}
939888Sandreas@sandberg.pp.se
949888Sandreas@sandberg.pp.seint
959888Sandreas@sandberg.pp.sefp80_isfinite(fp80_t fp80)
969888Sandreas@sandberg.pp.se{
979888Sandreas@sandberg.pp.se    return !fp80_isnan(fp80) && !fp80_isinf(fp80);
989888Sandreas@sandberg.pp.se}
999888Sandreas@sandberg.pp.se
1009888Sandreas@sandberg.pp.seint
1019888Sandreas@sandberg.pp.sefp80_isnan(fp80_t fp80)
1029888Sandreas@sandberg.pp.se{
1039888Sandreas@sandberg.pp.se    return fp80_issnan(fp80) || fp80_isqnan(fp80) ? fp80_sgn(fp80) : 0;
1049888Sandreas@sandberg.pp.se}
1059888Sandreas@sandberg.pp.se
1069888Sandreas@sandberg.pp.seint
1079888Sandreas@sandberg.pp.sefp80_iszero(fp80_t fp80)
1089888Sandreas@sandberg.pp.se{
1099899Sandreas@sandberg.pp.se    return fp80.repr.fi == 0 && FP80_EXP(fp80) == 0 ? fp80_sgn(fp80) : 0;
1109888Sandreas@sandberg.pp.se}
1119888Sandreas@sandberg.pp.se
1129888Sandreas@sandberg.pp.seint
1139888Sandreas@sandberg.pp.sefp80_isnormal(fp80_t fp80)
1149888Sandreas@sandberg.pp.se{
1159888Sandreas@sandberg.pp.se    return FP80_EXP(fp80) != 0 && !fp80_isspecial(fp80) ?
1169888Sandreas@sandberg.pp.se        fp80_sgn(fp80) : 0;
1179888Sandreas@sandberg.pp.se}
1189888Sandreas@sandberg.pp.se
1199888Sandreas@sandberg.pp.seint
1209888Sandreas@sandberg.pp.sefp80_issubnormal(fp80_t fp80)
1219888Sandreas@sandberg.pp.se{
1229888Sandreas@sandberg.pp.se    return FP80_FRAC(fp80) && FP80_EXP(fp80) == 0 ? fp80_sgn(fp80) : 0;
1239888Sandreas@sandberg.pp.se}
1249888Sandreas@sandberg.pp.se
1259888Sandreas@sandberg.pp.seint
1269888Sandreas@sandberg.pp.sefp80_classify(fp80_t fp80)
1279888Sandreas@sandberg.pp.se{
1289888Sandreas@sandberg.pp.se    if (fp80_issubnormal(fp80)) {
1299888Sandreas@sandberg.pp.se        return FP_SUBNORMAL;
1309888Sandreas@sandberg.pp.se    } else if (fp80_iszero(fp80)) {
1319888Sandreas@sandberg.pp.se        return FP_ZERO;
1329888Sandreas@sandberg.pp.se    } else if (fp80_isinf(fp80)) {
1339888Sandreas@sandberg.pp.se        return FP_INFINITE;
1349888Sandreas@sandberg.pp.se    } else if (fp80_isnan(fp80)) {
1359888Sandreas@sandberg.pp.se        return FP_NAN;
1369888Sandreas@sandberg.pp.se    } else {
1379888Sandreas@sandberg.pp.se        assert(fp80_isfinite(fp80));
1389888Sandreas@sandberg.pp.se        return FP_NORMAL;
1399888Sandreas@sandberg.pp.se    }
1409888Sandreas@sandberg.pp.se}
1419888Sandreas@sandberg.pp.se
1429888Sandreas@sandberg.pp.sedouble
1439888Sandreas@sandberg.pp.sefp80_cvtd(fp80_t fp80)
1449888Sandreas@sandberg.pp.se{
14510480SAndreas.Sandberg@ARM.com    return fp80_cvtfp64(fp80).value;
14610480SAndreas.Sandberg@ARM.com}
14710480SAndreas.Sandberg@ARM.com
14810480SAndreas.Sandberg@ARM.comfp64_t
14910480SAndreas.Sandberg@ARM.comfp80_cvtfp64(fp80_t fp80)
15010480SAndreas.Sandberg@ARM.com{
1519899Sandreas@sandberg.pp.se    const int sign = fp80.repr.se & FP80_SIGN_BIT;
1529888Sandreas@sandberg.pp.se
1539888Sandreas@sandberg.pp.se    if (!fp80_isspecial(fp80)) {
1549899Sandreas@sandberg.pp.se        const uint64_t frac = fp80.repr.fi;
1559888Sandreas@sandberg.pp.se        const int unb_exp = FP80_EXP(fp80) - FP80_EXP_BIAS;
1569888Sandreas@sandberg.pp.se        const int fp64_exp = unb_exp + FP64_EXP_BIAS;
1579888Sandreas@sandberg.pp.se        const uint64_t fp64_frac = frac >> (FP80_FRAC_BITS - FP64_FRAC_BITS);
1589888Sandreas@sandberg.pp.se
1599888Sandreas@sandberg.pp.se        if (fp64_exp > 0 && fp64_exp < FP64_EXP_SPECIAL) {
1609888Sandreas@sandberg.pp.se            /* These numbers fall in the range of what we can express
1619888Sandreas@sandberg.pp.se             * as normals */
1629888Sandreas@sandberg.pp.se            return build_fp64(sign, fp64_frac, fp64_exp);
1639888Sandreas@sandberg.pp.se        } else if (fp64_exp <= 0) {
1649888Sandreas@sandberg.pp.se            uint64_t fp64_denormal_frac = fp64_frac >> (-fp64_exp);
1659888Sandreas@sandberg.pp.se            /* Generate a denormal or zero */
1669888Sandreas@sandberg.pp.se            return build_fp64(sign, fp64_denormal_frac, 0);
1679888Sandreas@sandberg.pp.se        } else {
1689888Sandreas@sandberg.pp.se            /* Infinity */
1699888Sandreas@sandberg.pp.se            return build_fp64(sign, 0, FP64_EXP_SPECIAL);
1709888Sandreas@sandberg.pp.se        }
1719888Sandreas@sandberg.pp.se    } else {
1729888Sandreas@sandberg.pp.se        if (fp80_isinf(fp80)) {
1739888Sandreas@sandberg.pp.se            return build_fp64(sign, 0, FP64_EXP_SPECIAL);
1749888Sandreas@sandberg.pp.se        } else if (fp80_issnan(fp80)) {
17510480SAndreas.Sandberg@ARM.com            return fp80_sgn(fp80) > 0 ? fp64_snan : fp64_nsnan;
1769888Sandreas@sandberg.pp.se        } else if (fp80_isqnani(fp80)) {
17710480SAndreas.Sandberg@ARM.com            return fp64_qnani;
1789888Sandreas@sandberg.pp.se        } else {
1799888Sandreas@sandberg.pp.se            assert(fp80_isqnan(fp80));
18010480SAndreas.Sandberg@ARM.com            return fp80_sgn(fp80) > 0 ? fp64_qnan : fp64_nqnan;
1819888Sandreas@sandberg.pp.se        }
1829888Sandreas@sandberg.pp.se    }
1839888Sandreas@sandberg.pp.se}
1849888Sandreas@sandberg.pp.se
1859888Sandreas@sandberg.pp.sefp80_t
1869888Sandreas@sandberg.pp.sefp80_cvfd(double value)
1879888Sandreas@sandberg.pp.se{
1889888Sandreas@sandberg.pp.se    const fp64_t fp64 = { .value = value };
18910480SAndreas.Sandberg@ARM.com
19010480SAndreas.Sandberg@ARM.com    return fp80_cvffp64(fp64);
19110480SAndreas.Sandberg@ARM.com}
19210480SAndreas.Sandberg@ARM.com
19310480SAndreas.Sandberg@ARM.comfp80_t
19410480SAndreas.Sandberg@ARM.comfp80_cvffp64(fp64_t fp64)
19510480SAndreas.Sandberg@ARM.com{
1969888Sandreas@sandberg.pp.se    const uint64_t frac = FP64_FRAC(fp64);
1979888Sandreas@sandberg.pp.se    const unsigned exp = FP64_EXP(fp64);
1989888Sandreas@sandberg.pp.se    const int unb_exp = exp - FP64_EXP_BIAS;
1999888Sandreas@sandberg.pp.se    const uint64_t fp80_frac = frac << (FP80_FRAC_BITS - FP64_FRAC_BITS);
2009888Sandreas@sandberg.pp.se
2019888Sandreas@sandberg.pp.se    if (exp != 0) {
2029888Sandreas@sandberg.pp.se        // Normal, inf, nan
2039888Sandreas@sandberg.pp.se        const unsigned fp80_exp = exp == FP64_EXP_SPECIAL ?
2049888Sandreas@sandberg.pp.se            FP80_EXP_SPECIAL : (unb_exp + FP80_EXP_BIAS);
2059888Sandreas@sandberg.pp.se        const fp80_t fp80 = BUILD_FP80(fp64.bits & FP64_SIGN_BIT,
2069888Sandreas@sandberg.pp.se                                       fp80_frac, fp80_exp);
2079888Sandreas@sandberg.pp.se        return fp80;
2089888Sandreas@sandberg.pp.se    } else if (exp == 0 && frac == 0) {
2099888Sandreas@sandberg.pp.se        // Zero
2109888Sandreas@sandberg.pp.se        const fp80_t fp80 = BUILD_FP80(fp64.bits & FP64_SIGN_BIT, 0, 0);
2119888Sandreas@sandberg.pp.se        return fp80;
2129888Sandreas@sandberg.pp.se    } else {
2139888Sandreas@sandberg.pp.se        // Denormal
2149888Sandreas@sandberg.pp.se        uint64_t fp80_fi = fp80_frac;
2159888Sandreas@sandberg.pp.se        int shift_amt = 0;
2169888Sandreas@sandberg.pp.se        while (!(fp80_fi & FP80_INT_BIT)) {
2179888Sandreas@sandberg.pp.se            fp80_fi <<= 1;
2189888Sandreas@sandberg.pp.se            ++shift_amt;
2199888Sandreas@sandberg.pp.se        }
2209888Sandreas@sandberg.pp.se        const unsigned fp80_exp = (unb_exp - shift_amt) + FP80_EXP_BIAS;
2219888Sandreas@sandberg.pp.se        const fp80_t fp80 = BUILD_FP80(fp64.bits & FP64_SIGN_BIT,
2229888Sandreas@sandberg.pp.se                                       fp80_fi, fp80_exp);
2239888Sandreas@sandberg.pp.se        return fp80;
2249888Sandreas@sandberg.pp.se    }
2259888Sandreas@sandberg.pp.se}
2269888Sandreas@sandberg.pp.se
2279888Sandreas@sandberg.pp.sevoid
2289888Sandreas@sandberg.pp.sefp80_debug_dump(FILE *fout, fp80_t fp80)
2299888Sandreas@sandberg.pp.se{
2309888Sandreas@sandberg.pp.se    fprintf(fout, "sgn: %i, int: %i, frac: 0x%llx, exp: 0x%x (%i)\n",
2319899Sandreas@sandberg.pp.se            fp80_sgn(fp80), !!(fp80.repr.fi & FP80_INT_BIT), FP80_FRAC(fp80),
2329888Sandreas@sandberg.pp.se            FP80_EXP(fp80), FP80_EXP(fp80) - FP80_EXP_BIAS);
2339888Sandreas@sandberg.pp.se}
234