PreviousUpNext

15.4.1121  src/lib/std/src/math64-intel32.pkg

## math64-intel32.pkg
## *************************************************************************
##                                                                         * 
## Copyright (c) 1985 Regents of the University of California.             *
##                                                                         * 
## Use and reproduction of this software are granted  in  accordance  with *
## the terms and conditions specified in  the  Berkeley  Software  License *
## Agreement (in particular, this entails acknowledgement of the programs' *
## source, and inclusion of this notice) with the additional understanding *
## that  all  recipients  should regard themselves as participants  in  an *
## ongoing  research  project and hence should  feel  obligated  to report *
## their  experiences (good or bad) with these elementary function  codes, *
## using "sendbug 4bsd-bugs@BERKELEY", to the authors.                     *
##                                                                         *
## K.C. Ng, with Z-S. Alex Liu, S. McDonald, P. Tang, W. Kahan.            *
## Revised on 5/10/85, 5/13/85, 6/14/85, 8/20/85, 8/27/85, 9/11/85.        *
##                                                                         *
## *************************************************************************

# Compiled by:
#     src/lib/std/src/standard-core.sublib

# The following functions were adapted from the 4.3BSD math library.
# Eventually, each machine supported should have a hand-coded math
# generic with more efficient versions of these functions.


###                       "The world is full of magical things
###                        patiently waiting for our wits
###                        to grow sharper."
###
###                                    -- Bertrand Russell



package math64: (weak)  Math {                  # Math          is from   src/lib/std/src/math.api
                                                # inline_t      is from   src/lib/core/init/built-in.pkg

    Float = Float;

    infix my 50  ==== ; 

    my (+)     = inline_t::f64::(+);
    my (-)     = inline_t::f64::(-);
    my (*)     = inline_t::f64::(*);
    my (/)     = inline_t::f64::(/);
    my    (-_) = inline_t::f64::neg;
    my neg     = inline_t::f64::neg;
    my (<)     = inline_t::f64::(<);
    my (<=)    = inline_t::f64::(<=);
    my (>)     = inline_t::f64::(>);
    my (>=)    = inline_t::f64::(>=);
    my (====)  = inline_t::f64::(====);


    package i =  inline_t::default_int;                         # inline_t      is from   src/lib/core/init/built-in.pkg

    my lessu:  (Int, Int) -> Bool = i::ltu;

    pi = 3.14159265358979323846;
    e  = 2.7182818284590452354;

    fun is_nan x =  bool::not (x====x);

    plus_infinity = 1E300 * 1E300;
    minus_infinity = -plus_infinity;

    na_n = 0.0 / 0.0;

    two_to_the_54 = 18014398509481984.0;
    two_to_the_minus_54 = 1.0 / 18014398509481984.0;

    # This function is IEEE double-precision specific;
    # it works correctly on subnormal inputs and outputs;
    # we do not apply it to inf's and nan's:
    #
    fun scalb (x, k)
        = 
        {   j = runtime::asm::logb x; 
            k' = (i::(+))(k, j);

            if (j == -1023)

                scalb (x*two_to_the_54, (i::(-))(k, 54));               # 2

            elif (lessu((i::(+))(k', 1022), 2046))              

                runtime::asm::scalb (x, k);                             # 1

            elif ((i::(<))(k', 0))

                if ((i::(<))(k', (i::(-))(-1022, 54)))
                    0.0;                                                # 3
                else
                    scalb (x, (i::(+))(k, 54)) * two_to_the_minus_54;   # 4
                fi;
            else
                x * plus_infinity;                                      # 5
            fi;
        };

# Proof of correctness of scalb:      (Appel)
#    1. if x is normal and x*2^k is normal 
#          then case /*1*/ applies, computes right answer
#    2. if x is subnormal and x*2^k is normal
#          then case /*2*/ reduces problem to case 1.
#    3. if x*2^k is sub-subnormal (i.e. 0)
#          then case /*3*/ applies, returns 0.0
#    4. if x*2^k is subnormal
#          then -1076 < k' <= -1023, case /*4*/ applies,
#                computes right answer
#    5. if x*2^k is supernormal (i.e. infinity)
#          then case /*5*/ computes right answer

              
          



    # This function is IEEE double-precision specific;
    #  it works correctly on subnormal inputs;
    #  must not be applied to inf's and nan's
    #
    fun logb x
        =
        case (runtime::asm::logb x)
            -1023 => (i::(-))(runtime::asm::logb (x*two_to_the_54), 54); #  Denormalized number 
            i     => i;
        esac;


    negone = -1.0;

    zero = 0.0;
    half = 0.5;
    one  = 1.0;
    two  = 2.0;
    four = 4.0;

# * SHOULD BE INLINE OP *
   /* may be applied to inf's and nan's
      GETS MINUS-ZERO WRONG!                    XXX BUGGO FIXME
    */
    fun copysign (a, b)
        =
        case(a < zero, b < zero)
            (TRUE,     TRUE    ) =>  a;
            (FALSE,    FALSE   ) =>  a;
            _                    => -a;
        esac;


    # May be applied to inf's and nan's 
    #
    fun abs x
        =
        x < zero  ??  -x
                  ::   x;

    fun (mod) (a, b)
        =
        (i::(-))(a, (i::(*))(i::div (a, b), b));

    # We will never call floor with an inf or nan 
    #
    fun floor x
        =
        if (x < 1073741824.0 and x >= -1073741824.0)
                         runtime::asm::floor x;
        elif (is_nan x)  raise exception exceptions_guts::DOMAIN;               # exceptions_guts       is from   src/lib/std/src/exceptions-guts.pkg
        else             raise exception exceptions_guts::OVERFLOW;
        fi;

    real = inline_t::f64::from_tagged_int;

    # This is the IEEE double-precision maxint;
    # won't work accurately on VAX 
    #
    maxint = 4503599627370496.0;

    # realround (x) returns x rounded to some nearby integer, almost always
    # the nearest integer.
    #  May be applied to inf's and nan's.
    #
    fun realround x
        =
        x >= 0.0  ??   x+maxint-maxint
                  ::   x-maxint+maxint;

    pio4   =  7.853981633974483096E-1;
    pio2   =  1.5707963267948966192E0;
    pi3o4  =  2.3561944901923449288E0;
#    PI     =  pi;
    pi2    =  6.2831853071795864769E0;
    one_over2pi = 0.1591549430918953357688837633725143620345;


    stipulate

        p1 =  1.3887401997267371720E-2;
        p2 =  3.3044019718331897649E-5;
        q1 =  1.1110813732786649355E-1;
        q2 =  9.9176615021572857300E-4;

    herein

        fun exp__e (x: Float, c: Float)
            =
            {   z = x*x;
                p = z*(p1+z*p2);
                q = z*(q1+z*q2);
                xp= x*p; 
                xh= x*half;
                w = xh-(q-xp);
                c = c+x*((xh*w-(q-(p+p+xp)))/(one-w)+c);
                z*half+c;
            };
    end;

    # For exp and ln 
    ln2hi  =  6.9314718036912381649E-1;
    ln2lo  =  1.9082149292705877000E-10;
    sqrt2  =  1.4142135623730951455E0;
    lnhuge =  7.1602103751842355450E2;
    lntiny = -7.5137154372698068983E2;
    invln2 =  1.4426950408889633870E0;

    fun exp (x: Float)  # Propagates and generates inf's and nan's correctly 
        =
        {   fun exp_norm x
                =
                {   # Argument reduction:  x --> x - k*ln2 
                    k = floor (invln2*x+copysign (half, x)); #  k=NINT (x/ln2) 
                    kkk = real k;
                    #  express x-k*ln2 as z+c 
                    hi = x-kkk*ln2hi;
                    lo = kkk*ln2lo;
                    z = hi - lo;
                    c = (hi-z)-lo;
                    #  return 2^k*[expm1 (x) + 1] 
                    z = z + exp__e (z, c);
                    scalb (z+one, k);
                };

                if (x <= lnhuge) 
                                 x >= lntiny  ??  exp_norm x
                                              ::  zero;
                elif (is_nan x)  x;
                else             plus_infinity;
                fi;
        };

    stipulate

        c1 = 6.6666666666667340202E-1;
        c2 = 3.9999999999416702146E-1;
        c3 = 2.8571428742008753154E-1;
        c4 = 2.2222198607186277597E-1;
        c5 = 1.8183562745289935658E-1;
        c6 = 1.5314087275331442206E-1;
        c7 = 1.4795612545334174692E-1;

    herein

        fun log__l  z
            =
            z*(c1+z*(c2+z*(c3+z*(c4+z*(c5+z*(c6+z*c7))))));
    end;

    fun ln (x: Float)  #  handles inf's and nan's correctly 
        =
        if (x>0.0)

            if (x < plus_infinity)

                k = logb (x);
                x = scalb (x, (i::neg) k);

                my (k, x)
                    =
                    x >= sqrt2  ??  ((i::(+))(k, 1), x*half)
                                ::  (k, x);

                kkk = real k;
                x = x - one;

                # Compute log (1+x) 

                s = x/(two+x);
                t = x*x*half;
                z = kkk*ln2lo+s*(t+log__l (s*s));
                x = x + (z - t);

                kkk*ln2hi+x; 

            else
                x;
            fi;

        elif ((x ==== 0.0))

            minus_infinity;

        elif (is_nan x)

            x;
        else
            na_n;
        fi;

    one_overln10 = 1.0 / ln 10.0;


    fun log10 x
        =
        ln x * one_overln10;


    fun is_int y
        =
        realround (y)-y ==== 0.0;


    fun is_odd_int y
        =
        is_int((y - 1.0)*0.5);


    fun intpow (x, 0)
            =>
            1.0;

        intpow (x, y)
            =>
            {   h = i::rshift (y, 1);
                z = intpow (x, h);
                zz = z*z;

                if (y==(i::(+))(h, h))  zz;
                else                    x*zz;
                fi;
            };
    end;

    # We do not properly handle negative zeros.                         XXX BUGGO FIXME
    # Also, the copysign function works incorrectly on negative zero.   XXX BUGGO FIXME
    # The code for "pow" below should work correctly when these other 
    # bugs are fixed.  Andrew. Appel, 5/8/97 */
    #
    fun pow (x, y)
        =
        if (y > 0.0)

            if (y < plus_infinity) 

                if (x > minus_infinity)

                    if (x > 0.0)

                        exp (y*ln (x));

                    else
                        if (x ==== 0.0)

                            is_odd_int y  ??  x
                                          ::  0.0;

                        else
                            is_int y   ??   intpow (x, floor (y+0.5))
                                       ::   na_n;
                        fi;
                    fi;
                else
                    if   (is_nan x)       x;
                    elif (is_odd_int y)   x;
                    else                  plus_infinity;
                    fi;
                fi;
            else
                ax = abs (x);

                if   (ax > 1.0)  plus_infinity;
                elif (ax < 1.0)  0.0;
                else             na_n;
                fi;
            fi;
        else
            if (y < 0.0)

                if (y > minus_infinity)

                    if (x > minus_infinity)
                        if   (x > 0.0)          exp (y*ln (x));
                        elif   (x====0.0) 
                            if (is_odd_int y)   copysign (plus_infinity, x);
                            else                plus_infinity;
                            fi;
                        else
                            if (is_int y)       1.0 / intpow (x, floor(-y+0.5));
                            else                na_n;
                            fi;
                        fi;
                    else
                        if   (is_nan x)            x;
                        elif (is_odd_int y)     -0.0;
                        else                     0.0;
                        fi;
                    fi;
                else
                    ax = abs (x);

                    if   (ax > 1.0)              0.0;
                    elif (ax < 1.0)    plus_infinity;
                    else                        na_n;
                    fi;
                fi;
            else
                is_nan y  ??  y
                          ::  1.0;
            fi;
        fi;

    my (**) = pow;

    stipulate

        athfhi =  4.6364760900080611433E-1;
        athflo =  1.0147340032515978826E-18;
        at1hi =   0.78539816339744830676;
        at1lo =   1.11258708870781088040E-18;
        a1     =  3.3333333333333942106E-1;
        a2     = -1.9999999999979536924E-1;
        a3     =  1.4285714278004377209E-1;
        a4     = -1.1111110579344973814E-1;
        a5     =  9.0908906105474668324E-2;
        a6     = -7.6919217767468239799E-2;
        a7     =  6.6614695906082474486E-2;
        a8     = -5.8358371008508623523E-2;
        a9     =  4.9850617156082015213E-2;
        a10    = -3.6700606902093604877E-2;
        a11    =  1.6438029044759730479E-2;

        fun atn (t, hi, lo)     #  for -0.4375 <= t <= 0.4375 
            =
            {   z = t*t;

                hi+(t+(lo-t*(z*(a1+z*(a2+z*(a3+z*(a4+z*(a5+z*(a6+z*(a7+
                                    z*(a8+z*(a9+z*(a10+z*a11)))))))))))));
            };

        fun atan (t)    #  0 <= t <= 1 
             =
             if   (t <= 0.4375 ) atn (t, zero, zero);
             elif (t <= 0.6875 ) atn((t-half)/(one+half*t), athfhi, athflo);
             else                atn((t-one)/(one+t), at1hi, at1lo);
             fi;

        fun atanpy y #  y>=0 
            =
            if (y>one) pio2 - atan (one/y);
            else              atan y;
            fi;

        fun atan2pypx (x, y)
            = 
            if (y > x)   pio2 - atan (x/y);
            else                atan (y/x);
            fi;

        fun atan2py (x, y)
            = 
            if   (x > 0.0 )                     atan2pypx (x, y); 
            elif (x ==== 0.0 and y ==== 0.0 )   0.0;
            else                                pi - atan2pypx(-x, y);
            fi;

    herein

        fun atan y       #  miraculously handles inf's and nan's correctly 
            =
            if   (y <= 0.0)   -(atanpy(-y));
            else                atanpy y;
            fi;

        fun atan2 (y, x) #  miraculously handles inf's and nan's correctly 
            =
            if  (y >= 0.0)   atan2py (x, y);
            else           -(atan2py (x,-y));
            fi;
    end;


    sqrt =  math_inline_t::sqrt;
    sin  =  math_inline_t::sine;
    cos  =  math_inline_t::cosine;
    tan  =  math_inline_t::tangent;

    fun asin x =  atan2 (x, sqrt (1.0-x*x));
    fun acos x =  2.0 * atan2 (sqrt((1.0-x)/(1.0+x)), 1.0);

    fun cosh u
        =
        {   a = exp u;

            if   (a====0.0) 
                 plus_infinity;
            else 0.5 * (a + 1.0 / a);
            fi;
        };

    fun sinh u
        =
        {    a = exp u; 

             if (a ==== 0.0)   copysign (plus_infinity, u);
             else              0.5 * (a - 1.0 / a);
             fi;
        };

    fun tanh u
        =
        {   a = exp u; 
            b = 1.0 / a;

            if (a====0.0)  copysign (1.0, u);
            else           (a-b) / (a+b);
            fi;
        };
};




Comments and suggestions to: bugs@mythryl.org

PreviousUpNext