PreviousUpNext

15.4.1052  src/lib/std/src/eight-byte-float-guts.pkg

## eight-byte-float-guts.pkg

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

###                    "Science is what you know,
###                     philosophy is what you don't know."
###
###                                 -- Bertrand Russell 



package eight_byte_float_guts: (weak)  Float {          # Float is from   src/lib/std/src/float.api
    #
    package i=   inline_t::default_int;                 # inline_t      is from   src/lib/core/init/built-in.pkg

    package math= math64;                               # math64        is from   src/lib/std/src/math64-intel32.pkg

    infix my 50 ==== !=;

    Float = Float;
    
    fun *+(a: Float, b, c) =  a*b+c;
    fun *-(a: Float, b, c) =  a*b-c;

    my (====) =  inline_t::f64::(====);
    my (!=)   =  inline_t::f64::(!=);

    fun unordered (x: Float, y)
        =
        bool::not (x>y or x <= y);

    fun ?=== (x, y)
        =
        (x ==== y) or unordered (x, y);

    fun is_normal x
        =
        case (runtime::asm::logb x)
            #
            -1023 =>  FALSE;    #  0.0 or subnormal 
             1024 =>  FALSE;    #  inf or nan 
             _    =>  TRUE;
        esac;


    w31_r =  inline_t::f64::from_int1 o inline_t::i1::copy_tagged_unt;

    rbase = w31_r core_multiword_int::base;
    base_bits = inline_t::tu::copyt_tagged_int core_multiword_int::base_bits;
    intbound = w31_r 0ux40000000;       #  not necessarily the same as rbase 
    negintbound = -intbound;

    # The next three values are computed laboriously, partly to
    # avoid problems with inaccurate String->float conversions
    # in the compiler itself.          XXX BUGGO FIXME

    max_finite
        =
        g (0.0, y, 53)
        where
            fun f (x, i)
                =
                if (i==1023 ) x; else f (x*2.0, i + 1);fi;

            y = f (1.0, 0);

            fun g (z, y, 0) =>  z;
                g (z, y, i) =>  g (z+y, y*0.5, i - 1);
            end;
        end;

    min_normal_pos
        =
        f 1.0
        where
            fun f (x)
                =
                {   y =  x * 0.5;
                    #
                    if (is_normal y)   f y;
                    else               x;
                    fi;
                };
        end;

    stipulate

        # The intel32 uses extended precision (80 bits) internally, therefore 
        # it is necessary to write out the result of r * 0.5 to get 
        # 64 bit precision.

        mem =  inline_t::poly_rw_vector::array (1, min_normal_pos);
        set =  inline_t::poly_rw_vector::set;
        get =  inline_t::poly_rw_vector::check_sub;

        fun f ()
            =
            {   r = get (mem, 0);
                #
                y = r * 0.5;
                #
                set (mem, 0, y);
                #
                if (get (mem, 0) ==== 0.0)   r;
                else                         f ();
                fi;
            };
    herein
        min_pos = f();
    end;

    pos_inf = max_finite * max_finite;
    neg_inf = -pos_inf;

    fun is_finite x
        =
        neg_inf < x  and  x < pos_inf;

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

    fun floor x
        =
        if  (x <  intbound   and
             x >= negintbound
        )
            runtime::asm::floor x;
        else
            if (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;                          # exceptions_guts       is from   src/lib/std/src/exceptions-guts.pkg
            fi;
        fi;

    fun ceil     n =  -1 - floor (-1.0 - n);
    fun truncate n =  if (n < 0.0 ) ceil n; else floor n;fi;

    fun round x
        =
        # Ties round to the nearest even number:
        #
        {   fl = floor (x + 0.5);
            cl = ceil  (x - 0.5);

            if  (fl == cl)
                 fl;
            else
                 if  (tagged_unt_guts::bitwise_and (tagged_unt_guts::from_int fl, 0u1)  ==  0u1)
                      cl;
                 else
                      fl;
                 fi;
            fi;
        };

    # This is the IEEE double-
    # precision maxint: 
    #
    max_int = 4503599627370496.0;

    stipulate

        # realround mode x returns x rounded
        # to the nearest integer using the
        # given rounding mode.
        # May be applied to inf's and nan's.
        #
        fun floatround mode x
            =
            {   save_mode = ieee_float::get_rounding_mode ();

                ieee_float::set_rounding_mode mode;

                (if (x>=0.0 ) x+max_int-max_int; else x-max_int+max_int;fi)
                before
                    ieee_float::set_rounding_mode save_mode;
            };
    herein

        float_floor    =  floatround ieee_float::TO_NEGINF;
        float_ceil     =  floatround ieee_float::TO_POSINF;
        float_truncate =  floatround ieee_float::TO_ZERO;
        float_round    =  floatround ieee_float::TO_NEAREST;

    end;

    my abs:  Float -> Float
          =  inline_t::f64::abs;

    my from_int:  Int -> Float
               =  inline_t::f64::from_tagged_int;

    fun to_int ieee_float::TO_NEGINF  => floor;
        to_int ieee_float::TO_POSINF  => ceil;
        to_int ieee_float::TO_ZERO    => truncate;
        to_int ieee_float::TO_NEAREST => round;
    end;

    fun to_eight_byte_float x =  x;

    fun from_eight_byte_float _ x =  x;

    fun sign x
        =
        if   (x < 0.0)  -1;
        elif (x > 0.0)   1; 
        elif (is_nan x)  raise exception DOMAIN;
        else             0;
        fi;

    fun sign_bit x #  Bug: negative zero not handled properly  XXX BUGGO FIXME
        =
        runtime::asm::scalb (x, -(runtime::asm::logb x)) < 0.0;

    fun same_sign (x, y)
        =
        sign_bit x == sign_bit y;

    fun copy_sign (x, y)
        =                               # May not work if x is Nan.
        if (same_sign (x, y))  x;
        else                  -x;
        fi;

    fun compare (x, y)
        =
        if   (x < y   ) exceptions_guts::LESS;                                  # exceptions_guts       is from   src/lib/std/src/exceptions-guts.pkg
        elif (x > y   ) exceptions_guts::GREATER;
        elif (x ==== y) exceptions_guts::EQUAL; 
        else            raise exception ieee_float::UNORDERED_EXCEPTION;
        fi;
    
    fun compare_real (x, y)
        = 
        if   (x<y     ) ieee_float::LESS;
        elif (x>y     ) ieee_float::GREATER;
        elif (x ==== y) ieee_float::EQUAL; 
        else            ieee_float::UNORDERED;
        fi;

    # * This probably needs to be reorganized *  XXX BUGGO FIXME
    #
    fun ilk x
        =  #  Does not distinguish between quiet and signalling NaN 
        if (sign_bit x)
            if (x>neg_inf)      if   (x ==== 0.0)                     ieee_float::ZERO;
                                elif (runtime::asm::logb x == -1023)  ieee_float::SUBNORMAL;
                                else                                  ieee_float::NORMAL;
                                fi;
            elif (x====x)                                             ieee_float::INF;
            else                                                      ieee_float::NAN ieee_float::QUIET;
            fi;

         elif (x<pos_inf)      if (x ==== 0.0)                       ieee_float::ZERO;
                               elif (runtime::asm::logb x == -1023)  ieee_float::SUBNORMAL;
                               else                                  ieee_float::NORMAL;
                               fi;
         elif (x====x )                                              ieee_float::INF;
         else                                                        ieee_float::NAN ieee_float::QUIET;
         fi;

    radix = 2;
    precision = 53;                     #  hidden bit gets counted, too 

    two_to_the_neg_1000
        =
        f (1000, 1.0)
        where
            fun f (i, x)
                =
                if  (i == 0)  x;
                else          f (i - 1, x*0.5);   fi;
        end;

    # AARGH!  Our version of logb gives a value that's one less than the
    # rest of the world's logb functions.
    # We should fix this systematically some time. XXX BUGGO FIXME

    fun to_mantissa_exponent x 
        = 
        case (runtime::asm::logb x + 1)
          
             -1023 =>  if (x====0.0 ) { mantissa => x, exponent => 0 };
                       else  my { mantissa => m, exponent => e } = to_mantissa_exponent (x*1048576.0);
                                { mantissa => m, exponent => e - 20 };
                       fi;

              1024 => { mantissa => x,  exponent => 0 };

              i    => { mantissa => runtime::asm::scalb (x, -i), exponent => i };
        esac;

    fun from_mantissa_exponent { mantissa => m, exponent => e: Int }
        =
        if (m >= 0.5 and m <= 1.0  or m <= -0.5 and m >= -1.0)
           if (e > 1020)
                 if (e > 1050)
                     if (m>0.0) pos_inf;
                     else       neg_inf;
                     fi;
                 else
                      f (e - 1020,  runtime::asm::scalb (m, 1020))
                      where
                          fun f (i, x)
                              =
                              if (i==0)  x;
                              else       f (i - 1, x+x);
                              fi;
                      end;
                 fi;
            elif (e < -1020)
                   if (e < -1200 ) 0.0;
                   else
                        fun f (i, x)
                            =
                            if (i==0) x;
                            else      f (i - 1, x*0.5);
                            fi;

                        f (1020-e, runtime::asm::scalb (m, -1020));

                    fi;
           else
               runtime::asm::scalb (m, e);  #  This is the common case! 
           fi;
        else
             (to_mantissa_exponent m) -> { mantissa => m', exponent => e'     };
             from_mantissa_exponent      { mantissa => m', exponent => e' + e };
        fi;

    #  Some protection against insanity... 
    my _ =
        if (base_bits < 18 )  #  i.e., 3 * base_bits < 53 
            raise exception FAIL
                 "big digits in integer implementation do not have enough bits";
        fi;

    fun from_multiword_int (x:  multiword_int::Int)
        =
        {
            my core_multiword_int::BI { negative, digits } = core_multiword_int::concrete x;

            w2r = from_int o inline_t::tu::copyt_tagged_int;

            # Looking at at most 3 "big digits" is always
            # enough to get 53 bits of precision...
            # (See insanity insurance above.)

            fun dosign (x: Float)
                =
                if negative  -x;
                else          x;
                fi;

            fun calc (k, d1, d2, d3, [])
                    =>
                    dosign (runtime::asm::scalb (w2r d1 +
                                                rbase * (w2r d2 + rbase * w2r d3),
                                            k));
                calc (k, _, d1, d2, d3 ! r)
                    =>
                    calc (k + base_bits, d1, d2, d3, r);
            end;

            case digits
              
                [] => 0.0;
                [d] => dosign (w2r d);
                [d1, d2] => dosign (rbase * w2r d2 + w2r d1);
                d1 ! d2 ! d3 ! r => calc (0, d1, d2, d3, r);
            esac;
        };



    # whole and split could be implemented more efficiently if we had
    # control over the rounding mode; but for now we don't. XXX BUGGO FIXME

    fun whole x
        =
        if        (x > 0.0) 
            
             if   (x > 0.5)
                 
                  x - 0.5+max_int-max_int;
             else
                  whole (x+1.0) - 1.0;
             fi;
        else
             if        (x < 0.0)
                 
                  if   (x < -0.5)
                      
                       x + 0.5 - max_int + max_int;
                  else
                       whole (x - 1.0)+1.0;
                  fi;
             else
                  x;
             fi;
        fi;

    fun split x
        =
        {   w = whole x; 
            f = x-w;

            if (abs f ==== 1.0) { whole=>w+f, frac=>0.0 };
            else                { whole=>w,   frac=>f   };
            fi; 
        };

    fun float_mod  x
        =
        {   f = x - whole x;
          
            if (abs f ==== 1.0)   0.0;
            else                  f;
            fi;
        };

    fun rem (x, y)
        =
        y * .frac (split (x//y));

    fun check_float x
        =
        if   (x>neg_inf and x<pos_inf)  x;
        elif (is_nan x)                 raise exception exceptions_guts::DIVIDE_BY_ZERO;                # exceptions_guts       is from   src/lib/std/src/exceptions-guts.pkg
        else                            raise exception exceptions_guts::OVERFLOW;
        fi;

    fun to_multiword_int  mode  x
        =
        if   (is_nan x ) raise exception DOMAIN;
        elif (x ==== pos_inf or x ==== neg_inf ) raise exception OVERFLOW;
        else
             my (negative, x) =
                 if (x < 0.0 ) (TRUE, -x); else (FALSE, x);fi;
             fun feven x = .frac (split (x // 2.0)) ==== 0.0;

             # If the magnitute is less than 1.0, then
             # we just have to figure out whether to return -1, 0, or 1
             #
             if (x < 1.0 )
                 case mode   
                    ieee_float::TO_ZERO => 0;

                    ieee_float::TO_POSINF =>
                       if negative  0; else 1;fi;

                    ieee_float::TO_NEGINF =>
                       if negative  -1; else 0;fi;

                    ieee_float::TO_NEAREST =>
                       if   (x < 0.5 ) 0;
                       elif (x > 0.5 ) if negative  -1; else 1;fi;
                       else            0;
                       fi;
                 esac;  #  0 is even 
             else
                 # Otherwise we start with an integral value,
                 # suitably adjusted according to fractional part
                 # and rounding mode:

                 my { whole, frac } = split x;

                 start =
                     case mode   
                         ieee_float::TO_NEGINF =>
                           if (frac > 0.0 and negative)
                               whole + 1.0;
                           else whole;fi;

                        ieee_float::TO_POSINF =>
                           if (frac > 0.0 and not negative )
                               whole + 1.0;
                           else whole;fi;

                        ieee_float::TO_ZERO => whole;

                        ieee_float::TO_NEAREST =>
                           if   (frac > 0.5 )  whole + 1.0;
                           elif (frac < 0.5 )  whole;
                           elif (feven whole)  whole;
                           else                whole + 1.0;
                           fi;
                     esac;

                 # Now, for efficiency, we construct a
                 # fairly "small" whole number with
                 # all the significant bits.  First
                 # we get mantissa and exponent:

                 my { mantissa, exponent } = to_mantissa_exponent start;

                 # Then we adjust both to make sure the mantissa
                 # is whole:
                 # We know that man is between .5 and 1, so
                 # multiplying man by 2^53 will guarantee wholeness.
                 # However, exp might be < 53 -- which would be
                 # bad.  The correct solution is to multiply
                 # by 2^min (exp, 53) and adjust exp by subtracting
                 # min (exp, 53):

                 adj = int_guts::min (precision, exponent);
                 man = from_mantissa_exponent { mantissa, exponent => adj };
                 exponent = exponent - adj;

                 # Now we can construct our bignum digits by
                 # repeated div/mod using the bignum base.
                 # This loop will terminate after two rounds at
                 # the most because we chop off 30 bits each
                 # time:

                 fun loop x
                     =
                     if   (x ==== 0.0)

                          [];
                     else
                          my { whole, frac } = split (x // rbase);

                          dig = inline_t::tu::copyf_tagged_int
                                           (runtime::asm::floor
                                                (frac * rbase));

                          dig ! loop whole;
                     fi;

                 #  Now we make a bignum out of those digits: 
                 iman =
                     core_multiword_int::abstract
                         (core_multiword_int::BI { negative,
                                          digits => loop man } );

                 # Finally, we have to put the exponent back
                 # into the picture:

                 multiword_int_guts::(<<) (iman, inline_t::tu::copyf_tagged_int exponent);
            fi;
        fi;
  
    fun next_after _
        =
        raise exception FAIL "float::nextAfter unimplemented";

    my min:  (Float, Float) -> Float = inline_t::f64::min;
    my max:  (Float, Float) -> Float = inline_t::f64::max;

    fun to_decimal   _ =  raise exception FAIL "float::toDecimal unimplemented";
    fun from_decimal _ =  raise exception FAIL "float::fromDecimal unimplemented";

    format = float_format::format_float;
    to_string = format (number_string::GEN NULL);

    scan = number_scan::scan_real;
    from_string = number_string::scan_string scan;

    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::(>);
    my (<)   = inline_t::f64::(<);
    my (>=)  = inline_t::f64::(>=);
    my (<=)  = inline_t::f64::(<=);

};      #  float64 




Comments and suggestions to: bugs@mythryl.org

PreviousUpNext