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 20217 days, 15 hours, 52 minutes ago.