/** 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 } }