PreviousUpNext

15.4.1058  src/lib/std/src/float-format.pkg

## float-format.pkg

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

# Code for converting from real (IEEE 64-bit floating-point) to string.
# This ought to be replaced with David Gay's conversion algorithm.  XXX BUGGO FIXME

# This file is duplicated(?) as src/lib/src/float-format.pkg
# XXX BUGGO FIXME                       

package float_format
: (weak)
api {

    format_float:  number_string::Float_Format -> Float -> String;
        #
        # The type should be:                   XXX BUGGO FIXME
        #  my fmtReal:  number_string::Float_Format -> eight_byte_float::Float -> String


}
{
    package string= string_guts;                                # string_guts           is from   src/lib/std/src/string-guts.pkg
                                                                # inline_t              is from   src/lib/core/init/built-in.pkg

    infix val 50  ==== != ;

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

    fun floor x
        =
        if  (x <   1073741824.0
        and  x >= -1073741824.0
        )
            runtime::asm::floor  x;
        else
            raise exception exceptions_guts::OVERFLOW;          # exceptions_guts       is from   src/lib/std/src/exceptions-guts.pkg
        fi;

    real  = inline_t::f64::from_tagged_int;

    my (+)  =  string::(+);
    implode =  string::implode;
    cat     =  string::cat;
    length  =  string::length;

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

    fun inc i =  i::(+) (i, 1);
    fun dec i =  i::(-) (i, 1);

    fun min (i, j) =  if (i::(<) (i, j) ) i; else j; fi;
    fun max (i, j) =  if (i::(>) (i, j) ) i; else j; fi;

    atoi =  (num_format::format_int  number_string::DECIMAL)
            o
            inline_t::i1::from_int;

    fun zero_lpad (s, wid) =  number_string::pad_left  '0' wid s;
    fun zero_rpad (s, wid) =  number_string::pad_right '0' wid s;

    fun make_digit d
        =
        inline_t::vector_of_chars::get ("0123456789abcdef", d);


    # Decompose a non-zero real into a list of at most maxPrec significant digits
    # (the first digit non-zero), and integer exponent. The return value
    #   (a ! b ! c..., exp)
    # is produced from real argument
    #   a::bc... * (10 ^^ exp)
    # If the list would consist of all 9's, the list consisting of 1 followed by
    # all 0's is returned instead.
    #

    max_prec = 15;

    fun decompose (f, e, precision_g)
        =
        {
            fun scale_up (x, e)
                =
                if   (x < 1.0   )   scale_up (10.0*x, dec e);
                               else   (x, e);               fi;

            fun scale_dn (x, e)
                =
                if    (x >= 10.0   )   scale_dn (0.1*x, inc e);
                                  else   (x, e);              fi;

            fun mkdigits (f, 0, odd)
                    =>
                    ( [],
                      if        (f < 5.0 ) 0;
                      else if   (f > 5.0 ) 1;
                                        else odd;  fi;
                      fi
                    );

                mkdigits (f, i, _)
                    =>
                    {   d = floor f;

                        my (digits, carry)
                            =
                            mkdigits (10.0 * (f - real d), dec i,
                                                  i::mod (d, 2));

                        my (digit, c)
                            =
                            case (d, carry)
                              
                                 (9, 1) =>  (0, 1);
                                 _      =>  (i::(+) (d, carry), 0);
                            esac;


                        (digit ! digits, c);
                    };
            end;

            my (f, e)
                =
                if        (f <  1.0   )   scale_up (f, e);
                else if   (f >= 10.0  )   scale_dn (f, e);
                                     else            (f, e);   fi;
                fi;

            my (digits, carry)
                =
                mkdigits (f, max (0, min (precision_g e, max_prec)), 0);

            case carry
              
                 0 =>  (digits, e);
                 _ =>  (1 ! digits, inc e);
            esac;
        };

    fun float_fformat (r, prec)
        =
        {
            fun pf e = i::(+) (e, inc prec);

            fun rtoa (digits, e)
                =
                {
                    fun do_frac (_,  0, n, l) =>  prestring::rev_implode (n, l);
                        do_frac ([], p, n, l) =>  do_frac([], dec p, inc n, '0' ! l);

                        do_frac (hd ! tl, p, n, l)
                            =>
                            do_frac (tl, dec p, inc n, (make_digit hd) ! l);
                    end;

                    fun do_whole ([], e, n, l)
                            =>
                            if   (i::(>=) (e, 0))
                                 do_whole ([], dec e, inc n, '0' ! l);
                            else if   (prec == 0)
                                      prestring::rev_implode (n, l);
                                 else do_frac ([], prec, inc n, '.' ! l);fi;
                            fi;

                        do_whole (arg as (hd ! tl), e, n, l)
                            =>
                            if   (i::(>=) (e, 0))
                                 do_whole (tl, dec e, inc n, (make_digit hd) ! l);
                            else if   (prec == 0)
                                      prestring::rev_implode (n, l);
                                 else do_frac (arg, prec, inc n, '.' ! l); fi;
                            fi;
                    end;

                    fun do_zeros (_, 0, n, l) =>  prestring::rev_implode (n, l);
                        do_zeros (1, p, n, l) =>  do_frac (digits, p, n, l);
                        do_zeros (e, p, n, l) =>  do_zeros (dec e, dec p, inc n, '0' ! l);
                    end;

                    if   (i::(>=) (e, 0))
                         do_whole (digits, e, 0, []);
                    else if   (prec == 0)
                              "0";
                         else do_zeros (i::neg e, prec, 2, ['.', '0']);  fi;
                    fi;
                };

            if   (i::(<) (prec, 0)   )   raise exception exceptions_guts::SIZE;   fi;

            if   (r < 0.0)
                 { sign => "-", mantissa => rtoa (decompose(-r, 0, pf)) };
            else if   (r > 0.0)
                      { sign=>"", mantissa => rtoa (decompose (r, 0, pf)) };
                 else if   (prec == 0)
                           { sign=>"", mantissa => "0"};
                      else { sign=>"", mantissa => zero_rpad("0.", i::(+) (prec, 2)) }; fi;
                 fi;
            fi;
        };                      # fun float_fformat 

    fun float_eformat (r, prec)
        =
        {
            fun pf _ = inc prec;

            fun rtoa (sign, (digits, e))
                =
                {
                    fun make_res (m, e)
                        =
                        { sign,
                          mantissa =>  m,
                          exp      =>  e
                        };

                    fun do_frac (_,       0, l) =>  implode (list::reverse l);
                        do_frac ([],      n, l) =>  zero_rpad (implode (list::reverse l), n);
                        do_frac (hd ! tl, n, l) =>  do_frac (tl, dec n, (make_digit hd) ! l);
                    end;

                    if   (prec == 0)
                        
                         make_res (string::from_char (make_digit (list::head digits)), e);
                    else
                         make_res(
                             do_frac (list::tail digits, prec, ['.', make_digit (list::head digits)]),
                             e
                         );
                    fi;
                };

              if (i::(<) (prec, 0))
                  #                  
                  raise exception exceptions_guts::SIZE;
              fi;

              if (r < 0.0)
                  #
                  rtoa ("-", decompose(-r, 0, pf));
              else
                  if   (r > 0.0)
                       rtoa ("", decompose (r, 0, pf));
                  else if   (prec == 0)
                            { sign => "", mantissa => "0", exp => 0 };
                       else { sign => "", mantissa => zero_rpad("0.", i::(+) (prec, 2)), exp => 0 };fi;
                  fi;
              fi;
          };                                    #  fun float_eformat

    fun float_gformat (r, prec)
        =
        {
            fun pf _ = prec;

            fun rtoa (sign, (digits, e))
                =
                {
                    fun make_res (w, f, e)
                        =
                        { sign,
                          whole => w,
                          frac  => f,
                          exp   => e
                        };

                    fun do_frac [] => [];

                        do_frac (0 ! tl)
                            =>
                            case (do_frac tl)
                              
                                 []   =>  [];
                                 rest =>  '0' ! rest;
                            esac;

                        do_frac (hd ! tl)
                            =>
                            (make_digit hd) ! (do_frac tl);
                    end;

                    fun do_whole ([], e, wh)
                            =>
                            if   (i::(>=) (e, 0))
                                 do_whole([], dec e, '0' ! wh);
                            else make_res (implode (list::reverse wh), "", NULL);fi;

                        do_whole (arg as (hd ! tl), e, wh)
                            =>
                            if   (i::(>=) (e, 0))
                                 do_whole (tl, dec e, (make_digit hd) ! wh);
                            else make_res (implode (list::reverse wh), implode (do_frac arg), NULL);
                            fi;
                    end;

                    if  (i::(<)  (e,   -4)
                    or   i::(>=) (e, prec)
                    )
                          make_res(
                              string::from_char (make_digit (list::head digits)),
                              implode (do_frac (list::tail digits)),
                              THE e
                          );
                    else
                        if (i::(>=) (e, 0))
                            #
                            do_whole (digits, e, []);
                        else
                            frac = implode (do_frac digits);

                            make_res("0", zero_lpad (frac, i::(+) (length frac, i::(-) (-1, e))), NULL);
                        fi;
                    fi;
                };

            if   (i::(<) (prec, 1))   raise exception exceptions_guts::SIZE;   fi;              # exceptions_guts       is from   src/lib/std/src/exceptions-guts.pkg

            if (r < 0.0)
                #
                rtoa("-", decompose(-r, 0, pf));
            else
                if (r > 0.0)   rtoa("", decompose (r, 0, pf));
                else           { sign=>"", whole=>"0", frac=>"", exp=>NULL };
                fi;
            fi;
        };                                      # fun float_gformat

    infinity
        =
        bigger 100.0
        where
            fun bigger x
                =
                {   y = x*x; 
                    #
                    if (y > x)   bigger y;
                    else         x;
                    fi;
                };
        end;

   fun format_inf_nan x
       =
       if   (x ====  infinity)  "inf";
       elif (x ==== -infinity) "-inf";
       else                     "nan";
       fi;

    # Convert a real number to a string of
    # the form [-]d::dddE[-]dd, where the
    # precision (number of fractional digits)
    # is specified by the second argument:
    #
    fun real_to_sci_string prec r
        = 
        if (-infinity < r and r < infinity)
            
             my { sign, mantissa, exp } = float_eformat (r, prec);
          
             # Minimum size exponent string, no padding:
             #
             cat [sign, mantissa, "E", atoi exp];

        else
             format_inf_nan r;
        fi;

    # Convert a real number to a string of
    # the form [-]ddd::ddd, where the
    # precision (number of fractional digits)
    # is specified by the second argument:
    #
    fun real_to_fix_string prec x
        = 
        if (-infinity < x and x < infinity)
            
             my { sign, mantissa } = float_fformat (x, prec);
          
             sign + mantissa;                   # This '+' is string concatenation.
        else
             format_inf_nan x;
        fi;

      fun real_to_gen_string prec r 
          = 
          if (-infinity < r and r < infinity)
              
               my { sign, whole, frac, exp }
                   =
                   float_gformat (r, prec);

               my (frac, exp_string)
                   =
                   case exp
                     
                        NULL => if (frac == "")
                                     (".0", "");
                                else ("." + frac, "");  fi;

                        THE e => {
                           exp_string
                               =
                               if (i::(<) (e, 0))
                                    "E-" + zero_lpad (atoi (i::neg e), 2);
                               else "E" + zero_lpad (atoi e, 2);fi;

                             ( if (frac == "" ) ""; else "." + frac;fi,
                               exp_string
                             );
                           };
                     esac;


                cat [sign, whole, frac, exp_string];

          else
               format_inf_nan r;
          fi;

    fun format_float (number_string::SCI NULL)       => real_to_sci_string 6;
        format_float (number_string::SCI (THE prec)) => real_to_sci_string prec;
        format_float (number_string::FIX NULL)       => real_to_fix_string 6;
        format_float (number_string::FIX (THE prec)) => real_to_fix_string prec;
        format_float (number_string::GEN NULL)       => real_to_gen_string 12;
        format_float (number_string::GEN (THE prec)) => real_to_gen_string prec;

        format_float number_string::EXACT
            =>
            raise exception FAIL "RealFormat: format_float: EXACT not supported";
    end;
};



Comments and suggestions to: bugs@mythryl.org

PreviousUpNext