Module:Radix
Documentation for this module may be created at Module:Radix/doc
local r = {}
r.decimal_to = function(frame)
digits = {}
digitchars = {}
-- Get number (in base 10)
decimal = tonumber(frame.args[1])
-- If decimal is 0 or 1, return decimal
if decimal == 0 or decimal == 1 then
return tostring(decimal)
end
-- Separator
format_output = format_output and format_output or true
thousands = thousands and thousands or ","
point = point and point or "."
-- Get radix to convert to
radix = tonumber(frame.args[2])
-- sign is equal to sign(decimal) if radix is positive, and 1 otherwise
sign = radix > 0 and decimal / math.abs(decimal) or 1
-- If radix is not an integer: Round to nearest integer for now
radix = radix < 0 and math.ceil(radix) or math.floor(radix)
-- If abs(radix) is 0 or 1, return decimal
if radix == 0 or math.abs(radix) == 1 then
return tostring(decimal)
end
-- characters are based on other proglangs' parsing
characters = numerals and numerals or "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-- If abs(radix) > characters.len, return decimal
if math.abs(radix) > string.len(characters) then
return tostring(decimal)
end
-- precision is the number of digits below 1 to use
-- if decimal is less than 1/radix ^ precision, return 0
precision = tonumber(frame.args[3]) or 0
minpower = radix ^ (precision * -1)
if math.abs(decimal) < minpower then
return "0"
end
-- maxpower calculation
maxpower = radix ^ math.ceil(math.log(math.abs(decimal + 0.0))/math.log(math.abs(radix + 0.0)))
-- if decimal < 0 then maxpower should be 1
-- calculate digits
digitval = maxpower
i = 1
-- format radix and set up num of integral digits for possible formatting
if radix > 0 then decimal = math.abs(decimal) end
integrals = 0
nextdigitcarry = false
while math.abs(digitval) >= math.abs(minpower) do
-- get floor of abs(decimal) / abs (digitval)
thisdigit = digitval < 0 and math.ceil(math.abs(decimal)/digitval) or math.floor(math.abs(decimal)/digitval)
thisdigitsign = digitval / math.floor(digitval)
-- if thisdigit is negative, flip it and set nextdigitcarry to true
-- otherwise, set it to false
if nextdigitcarry then
thisdigit = math.abs(thisdigit) + 1
if digitval == 1 then thisdigit = thisdigit - 1 end
nextdigitcarry = true
else
nextdigitcarry = false
end
-- if i is 1 and thisdigit is 0:
-- if radix is negative and digitval is positive, add 1 to thisdigit
-- if radix is positive and maxpower isn't 1, skip
add_digit = true
if thisdigit == 0 and i == 1 then
if radix < 0 and digitval > 0 then
thisdigit = 1
nextdigitcarry = true
else
if math.abs(maxpower) > 1.0 then
add_digit = false
end
end
end
if add_digit then
-- add thisdigit to digits
digits[i] = thisdigit
-- subtract thisdigit * digitval from decimal
decimal = decimal - thisdigit * digitval
if digitval >= 1 then integrals = integrals + 1 end
-- add this digit to assembled string
i = i + 1
end
digitval = digitval / radix
end
-- if sign is -1, append a negative sign to the front
signchar = sign == -1 and "-" or ""
ret = "" .. signchar
-- assemble digits
separators = 0
distance = math.abs(radix) <= 6 and 4 or 3
for j,digit in ipairs(digits) do
digitchars[j+separators] = string.sub(characters, digit+1, digit+1)
-- when integrals - j is a positive multiple of distance
if format_output then
if j < integrals and (integrals - j) % distance == 0 then
-- add "," and 1 to separators
separators = separators + 1
digitchars[j+separators] = thousands
end
end
-- when j equals integrals and precision > 0
if j == integrals and precision > 0 then
-- add "." and 1 to separators
separators = separators + 1
digitchars[j+separators] = point
end
end
-- assemble final number
for k,character in ipairs(digitchars) do
ret = ret .. character
end
return ret
end
return r