PreviousUpNext

15.4.1085  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                       

stipulate
    package di  =  inline_t::default_int;               # inline_t              is from   src/lib/core/init/built-in.pkg
    package it  =  inline_t;                            # inline_t              is from   src/lib/core/init/built-in.pkg
    package ns  =  number_string;                       # number_string         is from   src/lib/std/src/number-string.pkg
    package ps  =  protostring;                         # protostring           is from   src/lib/std/src/protostring.pkg
    package sg  =  string_guts;                         # string_guts           is from   src/lib/std/src/string-guts.pkg
    package g2d =  exceptions_guts;                     # exceptions_guts       is from   src/lib/std/src/exceptions-guts.pkg
herein

    package float_format
    : (weak)
    api {

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


    }
    {
        infix my 50  ==== != ;

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

        fun floor x
            =
            if  (x <   1073741824.0
            and  x >= -1073741824.0
            )
                runtime::asm::floor  x;
            else
                raise exception g2d::OVERFLOW;
            fi;

        real  = it::f64::from_tagged_int;

        (+)  =  sg::(+);

        implode =  sg::implode;
        cat     =  sg::cat;
        length  =  sg::length_in_bytes;


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

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

        atoi =  (number_format::format_int  ns::DECIMAL)
                o
                it::i1::from_int;

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

        fun make_digit d
            =
            it::vector_of_chars::get_byte_as_char ("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;
                          elif (f > 5.0)   1;
                          else             odd;
                          fi
                        );

                    mkdigits (f, i, _)
                        =>
                        {   d = floor f;
                            #
                            my (digits, carry)
                                =
                                mkdigits (10.0 * (f - real d), dec i,
                                                      di::mod (d, 2));

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


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

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

                (mkdigits (f, max (0, min (precision_g e, max_prec)), 0))
                    ->
                    (digits, carry);

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

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

                fun rtoa (digits, e)
                    =
                    {
                        fun do_frac (_,  0, n, l) =>  ps::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 (di::(>=) (e, 0))   do_whole ([], dec e, inc n, '0' ! l);
                                elif   (prec == 0)     ps::rev_implode (n, l);
                                else                   do_frac ([], prec, inc n, '.' ! l);
                                fi;

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

                        fun do_zeros (_, 0, n, l) =>  ps::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   (di::(>=) (e, 0))   do_whole (digits, e, 0, []);
                        elif (prec == 0)         "0";
                        else                     do_zeros (di::neg e, prec, 2, ['.', '0']);
                        fi;
                    };

                if (di::(<) (prec, 0))   raise exception g2d::SIZE;   fi;

                if   (r < 0.0)   { sign => "-", mantissa => rtoa (decompose(-r, 0, pf)) };
                elif (r > 0.0)   { sign=>"", mantissa => rtoa (decompose (r, 0, pf)) };
                elif (prec == 0) { sign=>"", mantissa => "0"};
                else             { sign=>"", mantissa => zero_rpad("0.", di::(+) (prec, 2)) };
                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 (sg::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 (di::(<) (prec, 0))
                      #                  
                      raise exception g2d::SIZE;
                  fi;

                  if   (r < 0.0)     rtoa ("-", decompose(-r, 0, pf));
                  elif (r > 0.0)     rtoa ("", decompose (r, 0, pf));
                  elif (prec == 0)   { sign => "", mantissa => "0", exp => 0 };
                  else               { sign => "", mantissa => zero_rpad("0.", di::(+) (prec, 2)), exp => 0 };
                  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 (di::(>=) (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 (di::(>=) (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  (di::(<)  (e,   -4)
                        or   di::(>=) (e, prec)
                        )
                              make_res(
                                  sg::from_char (make_digit (list::head digits)),
                                  implode (do_frac (list::tail digits)),
                                  THE e
                              );
                        else
                            if (di::(>=) (e, 0))
                                #
                                do_whole (digits, e, []);
                            else
                                frac = implode (do_frac digits);

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

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

                if   (r < 0.0)   rtoa("-", decompose(-r, 0, pf));
                elif (r > 0.0)   rtoa("", decompose (r, 0, pf));
                else             { sign=>"", whole=>"0", frac=>"", exp=>NULL };
                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)
                #
                (float_eformat (r, prec))
                    ->
                    { sign, mantissa, exp };

                cat [sign, mantissa, "E", atoi exp];            # Minimum size exponent string, no padding.
            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)
                #
                (float_fformat (x, prec))
                    ->
                    { sign, mantissa };

                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)
                #
                (float_gformat (r, prec))
                    ->
                    { sign, whole, frac, exp };


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

                        THE e => {
                           exp_string
                               =
                               if (di::(<) (e, 0))   "E-" + zero_lpad (atoi (di::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 (ns::SCI NULL)       =>  real_to_sci_string 6;
            format_float (ns::SCI (THE prec)) =>  real_to_sci_string prec;
            format_float (ns::FIX NULL)       =>  real_to_fix_string 6;
            format_float (ns::FIX (THE prec)) =>  real_to_fix_string prec;
            format_float (ns::GEN NULL)       =>  real_to_gen_string 12;
            format_float (ns::GEN (THE prec)) =>  real_to_gen_string prec;

            format_float ns::EXACT
                =>
                raise exception DIE "RealFormat: format_float: EXACT not supported";
        end;
    };
end;


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext