[funini.com] -> [kei@sodan] -> Kernel Reading

root/arch/x86/math-emu/poly_l2.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. poly_l2
  2. poly_l2p1
  3. log2_kernel

/*---------------------------------------------------------------------------+
 |  poly_l2.c                                                                |
 |                                                                           |
 | Compute the base 2 log of a FPU_REG, using a polynomial approximation.    |
 |                                                                           |
 | Copyright (C) 1992,1993,1994,1997                                         |
 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
 |                  E-mail   billm@suburbia.net                              |
 |                                                                           |
 |                                                                           |
 +---------------------------------------------------------------------------*/

#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "fpu_system.h"
#include "control_w.h"
#include "poly.h"

static void log2_kernel(FPU_REG const *arg, u_char argsign,
                        Xsig * accum_result, long int *expon);

/*--- poly_l2() -------------------------------------------------------------+
 |   Base 2 logarithm by a polynomial approximation.                         |
 +---------------------------------------------------------------------------*/
void poly_l2(FPU_REG *st0_ptr, FPU_REG *st1_ptr, u_char st1_sign)
{
        long int exponent, expon, expon_expon;
        Xsig accumulator, expon_accum, yaccum;
        u_char sign, argsign;
        FPU_REG x;
        int tag;

        exponent = exponent16(st0_ptr);

        /* From st0_ptr, make a number > sqrt(2)/2 and < sqrt(2) */
        if (st0_ptr->sigh > (unsigned)0xb504f334) {
                /* Treat as  sqrt(2)/2 < st0_ptr < 1 */
                significand(&x) = -significand(st0_ptr);
                setexponent16(&x, -1);
                exponent++;
                argsign = SIGN_NEG;
        } else {
                /* Treat as  1 <= st0_ptr < sqrt(2) */
                x.sigh = st0_ptr->sigh - 0x80000000;
                x.sigl = st0_ptr->sigl;
                setexponent16(&x, 0);
                argsign = SIGN_POS;
        }
        tag = FPU_normalize_nuo(&x);

        if (tag == TAG_Zero) {
                expon = 0;
                accumulator.msw = accumulator.midw = accumulator.lsw = 0;
        } else {
                log2_kernel(&x, argsign, &accumulator, &expon);
        }

        if (exponent < 0) {
                sign = SIGN_NEG;
                exponent = -exponent;
        } else
                sign = SIGN_POS;
        expon_accum.msw = exponent;
        expon_accum.midw = expon_accum.lsw = 0;
        if (exponent) {
                expon_expon = 31 + norm_Xsig(&expon_accum);
                shr_Xsig(&accumulator, expon_expon - expon);

                if (sign ^ argsign)
                        negate_Xsig(&accumulator);
                add_Xsig_Xsig(&accumulator, &expon_accum);
        } else {
                expon_expon = expon;
                sign = argsign;
        }

        yaccum.lsw = 0;
        XSIG_LL(yaccum) = significand(st1_ptr);
        mul_Xsig_Xsig(&accumulator, &yaccum);

        expon_expon += round_Xsig(&accumulator);

        if (accumulator.msw == 0) {
                FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
                return;
        }

        significand(st1_ptr) = XSIG_LL(accumulator);
        setexponent16(st1_ptr, expon_expon + exponent16(st1_ptr) + 1);

        tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign ^ st1_sign);
        FPU_settagi(1, tag);

        set_precision_flag_up();        /* 80486 appears to always do this */

        return;

}

/*--- poly_l2p1() -----------------------------------------------------------+
 |   Base 2 logarithm by a polynomial approximation.                         |
 |   log2(x+1)                                                               |
 +---------------------------------------------------------------------------*/
int poly_l2p1(u_char sign0, u_char sign1,
              FPU_REG * st0_ptr, FPU_REG * st1_ptr, FPU_REG * dest)
{
        u_char tag;
        long int exponent;
        Xsig accumulator, yaccum;

        if (exponent16(st0_ptr) < 0) {
                log2_kernel(st0_ptr, sign0, &accumulator, &exponent);

                yaccum.lsw = 0;
                XSIG_LL(yaccum) = significand(st1_ptr);
                mul_Xsig_Xsig(&accumulator, &yaccum);

                exponent += round_Xsig(&accumulator);

                exponent += exponent16(st1_ptr) + 1;
                if (exponent < EXP_WAY_UNDER)
                        exponent = EXP_WAY_UNDER;

                significand(dest) = XSIG_LL(accumulator);
                setexponent16(dest, exponent);

                tag = FPU_round(dest, 1, 0, FULL_PRECISION, sign0 ^ sign1);
                FPU_settagi(1, tag);

                if (tag == TAG_Valid)
                        set_precision_flag_up();        /* 80486 appears to always do this */
        } else {
                /* The magnitude of st0_ptr is far too large. */

                if (sign0 != SIGN_POS) {
                        /* Trying to get the log of a negative number. */
#ifdef PECULIAR_486             /* Stupid 80486 doesn't worry about log(negative). */
                        changesign(st1_ptr);
#else
                        if (arith_invalid(1) < 0)
                                return 1;
#endif /* PECULIAR_486 */
                }

                /* 80486 appears to do this */
                if (sign0 == SIGN_NEG)
                        set_precision_flag_down();
                else
                        set_precision_flag_up();
        }

        if (exponent(dest) <= EXP_UNDER)
                EXCEPTION(EX_Underflow);

        return 0;

}

#undef HIPOWER
#define HIPOWER 10
static const unsigned long long logterms[HIPOWER] = {
        0x2a8eca5705fc2ef0LL,
        0xf6384ee1d01febceLL,
        0x093bb62877cdf642LL,
        0x006985d8a9ec439bLL,
        0x0005212c4f55a9c8LL,
        0x00004326a16927f0LL,
        0x0000038d1d80a0e7LL,
        0x0000003141cc80c6LL,
        0x00000002b1668c9fLL,
        0x000000002c7a46aaLL
};

static const unsigned long leadterm = 0xb8000000;

/*--- log2_kernel() ---------------------------------------------------------+
 |   Base 2 logarithm by a polynomial approximation.                         |
 |   log2(x+1)                                                               |
 +---------------------------------------------------------------------------*/
static void log2_kernel(FPU_REG const *arg, u_char argsign, Xsig *accum_result,
                        long int *expon)
{
        long int exponent, adj;
        unsigned long long Xsq;
        Xsig accumulator, Numer, Denom, argSignif, arg_signif;

        exponent = exponent16(arg);
        Numer.lsw = Denom.lsw = 0;
        XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg);
        if (argsign == SIGN_POS) {
                shr_Xsig(&Denom, 2 - (1 + exponent));
                Denom.msw |= 0x80000000;
                div_Xsig(&Numer, &Denom, &argSignif);
        } else {
                shr_Xsig(&Denom, 1 - (1 + exponent));
                negate_Xsig(&Denom);
                if (Denom.msw & 0x80000000) {
                        div_Xsig(&Numer, &Denom, &argSignif);
                        exponent++;
                } else {
                        /* Denom must be 1.0 */
                        argSignif.lsw = Numer.lsw;
                        argSignif.midw = Numer.midw;
                        argSignif.msw = Numer.msw;
                }
        }

#ifndef PECULIAR_486
        /* Should check here that  |local_arg|  is within the valid range */
        if (exponent >= -2) {
                if ((exponent > -2) || (argSignif.msw > (unsigned)0xafb0ccc0)) {
                        /* The argument is too large */
                }
        }
#endif /* PECULIAR_486 */

        arg_signif.lsw = argSignif.lsw;
        XSIG_LL(arg_signif) = XSIG_LL(argSignif);
        adj = norm_Xsig(&argSignif);
        accumulator.lsw = argSignif.lsw;
        XSIG_LL(accumulator) = XSIG_LL(argSignif);
        mul_Xsig_Xsig(&accumulator, &accumulator);
        shr_Xsig(&accumulator, 2 * (-1 - (1 + exponent + adj)));
        Xsq = XSIG_LL(accumulator);
        if (accumulator.lsw & 0x80000000)
                Xsq++;

        accumulator.msw = accumulator.midw = accumulator.lsw = 0;
        /* Do the basic fixed point polynomial evaluation */
        polynomial_Xsig(&accumulator, &Xsq, logterms, HIPOWER - 1);

        mul_Xsig_Xsig(&accumulator, &argSignif);
        shr_Xsig(&accumulator, 6 - adj);

        mul32_Xsig(&arg_signif, leadterm);
        add_two_Xsig(&accumulator, &arg_signif, &exponent);

        *expon = exponent + 1;
        accum_result->lsw = accumulator.lsw;
        accum_result->midw = accumulator.midw;
        accum_result->msw = accumulator.msw;

}

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading