floatingPointBase.frink

Download or view floatingPointBase.frink in plain text format


/** This function converts a floating-point number into a string
    representation in the specified base with the specified number of
    characters of precision.

    TODO:  Add units of measure?

    args: [n, base, digits]
      n: The number to convert

      base:  The base to convert to, specified as an integer from 2 to 36, or
             a string containing a custom alphabet like "0123456789AB".  The
             alphabet should not contain a period "."

      digits:  The number of digits (in the specified base) that
         will be produced in the string result.  
         Note that if the precision of the original number is less than the
         requested digits, some of the digits in the requested base will be
         illusory.

         TODO:  Make the number of digits try to echo the number of significant
         digits in n?

    returns:
       A string representing the number n in the specified base.
*/

base[n, base, digits] :=
{
   if isInteger[n]
      return base[n, base]

   signum = realSignum[n]
   str = (signum < 0 ? "-" : "")

   n = abs[n]
   intPart = trunc[n]

   // Convert the integer part to the specified base
   intStr = base[intPart, base]
   str = str + intStr + "."
   
   // Calculate how many more digits needed
   digits = max[0, digits - length[intStr]]

   if isString[base]
      baseDigits = length[base]
   else
      baseDigits = base
      
   // 1.  shift the radix point to the left in the base you're working
   // in (e.g.  if you're working in base 3, and want at least 20 digits in
   // your result, you multiply your numerator by 3^20)
   shift = baseDigits^digits

   prec = getPrecision[]
   try
   {
      // Calculate how many digits we need to calculate in the specified base.
      newPrecision = ceil[digits * (ln[baseDigits]/ln[10]) + 4]
      // println["newPrecision is $newPrecision"]
      setPrecision[newPrecision]

      n = n - intPart   // n now contains the fractional part

      // 2. Do the shift left and take the integer part.
      shifted = round[n * shift]

      // 3. Convert the resulting integer to the base using integer functions.
      resStr = base[shifted, base]

      // 4. Pad the fractional part with the necessary number of zeroes.
      str = str + padLeft[resStr, digits, "0"]
   }
   finally
   {
      setPrecision[prec]
   }

   return str
}


/** Parse a string containing a floating-point number in the specified base.

    THINK ABOUT:
    Since a floating-point number specified in one base quite likely cannot be
    exactly represented in another base with a finite number of digits, have a
    parameter to set requested significant digits in result?  It is currently
    limited by the division and the current value of setPrecision[]

    args:
    [str, base, digits]:
      str:  A string containing the number to parse.  This may contain a
            positive or negative sign, an (optional) decimal point, and
            optional underscores to separate digits.

      base:  An integer from 2 to 36 indicating the base that the string is
             represented in, or a string containing a custom alphabet like
            "0123456789AB".  The alphabet should not contain a period "."

      digits:  An optional argument that indicates the number of digits to
            return in the result.

            If digits is undef (the default), this will return an exact
            rational number indicating the exact value represented by the
            string.  This is the most precise interpretation.
            
            If digits is 0, this will attempt to return a number with the
            approximate number of significant digits that are contained in the
            number (plus up to 2 decimal digits.)  All digits will not be exact
            in this case.
*/

parseFloat[str, base, digits=undef] :=
{
   str =~ %s/_//g    // Remove optional underscores

   if isString[base]
      baseDigits = length[base]
   else
      baseDigits = base
      
   if [sign, intPart, fracPart] = str =~ %r/([\-\+]?)([^.]*)\.?([^.]*)/
   {
      n = 0.
      if digits == 0
         digits = ceil[(length[intPart] + length[fracPart]) * ln[baseDigits]/ln[10]+2]
      
      if digits == undef
      {
         n = 0          // Return an exact rational number.
         // 0 digits of precision would actually work in this case because
         // we'll only have integers and rational numbers.
         digits = getPrecision[]
      }

      // println["part 1, n is $n"]
      if intPart != ""
         n = n + parseInt[intPart, base]

      // println["part 2, n is $n, fracPart is $fracPart"]
      
      origPrec = getPrecision[]
      try
      {
         setPrecision[digits]
         if fracPart != ""
            n = n + parseInt[fracPart, base] / (baseDigits^ length[fracPart])

         // println["part 3, n is $n"]

         if sign == "-"
            return -n
         else
            return n
       }
       finally
       {
          setPrecision[origPrec]
       }
      
   } else
   {
      println["parseFloat: unmatched string $str"]
      return undef
   }
}


Download or view floatingPointBase.frink in plain text format


This is a program written in the programming language Frink.
For more information, view the Frink Documentation or see More Sample Frink Programs.

Alan Eliasen was born 20139 days, 7 hours, 2 minutes ago.