Module:BaseConvert: Difference between revisions
Appearance
Content deleted Content added
trim first argument in a-to-b syntax |
clean whitespace so diff with sandbox works |
||
Line 4: | Line 4: | ||
function normalizeFullWidthChars(s) |
function normalizeFullWidthChars(s) |
||
return mw.ustring.gsub(s, '[!-~]', function(s) |
|||
return mw.ustring.char(mw.ustring.codepoint(s, 1) - 0xFEE0) |
|||
end) |
|||
end |
end |
||
function _convert(n, base, from, precision, width, default, prefix, suffix) |
function _convert(n, base, from, precision, width, default, prefix, suffix) |
||
n = '' .. n -- convert to a string |
|||
-- strip off any leading '0x' (unless x is a valid digit in the input base) |
|||
from = tonumber(from) |
|||
if not from or from < 34 then |
|||
local c |
|||
n, c = n:gsub('^(-?)0[Xx]', '%1') |
|||
if c > 0 and not from then from = 16 end |
|||
end |
|||
-- strip off any leading '0x' (unless x is a valid digit in the input base) |
|||
from = tonumber(from) |
|||
-- because tonumber doesn't support negative numbers in non-10 bases. |
|||
if not from or from < 34 then |
|||
local c |
|||
n, c = n:gsub('^(-?)0[Xx]', '%1') |
|||
if c > 0 and not from then from = 16 end |
|||
end |
|||
-- replace any full-width Unicode characters in the string with their ASCII equivalents |
|||
n = normalizeFullWidthChars(n) |
|||
-- handle scientific notation with whitespace around the 'e' e.g. '5 e7' |
|||
n = n:gsub('%s*[eE]%s*', 'e') |
|||
from = from or 10 |
|||
local num = tonumber(n, from) |
|||
base = tonumber(base) |
|||
precision = tonumber(precision) |
|||
width = tonumber(width) |
|||
if not num or not base then return default or n end |
|||
local i, f = math.modf(num) |
|||
-- check for a negative sign. Do this while the input is still in string form, |
|||
local t = {} |
|||
-- because tonumber doesn't support negative numbers in non-10 bases. |
|||
repeat |
|||
local sign = '' |
|||
local c |
|||
i = math.floor(i / base) |
|||
n, c = n:gsub('^-', '') |
|||
table.insert(t, 1, digits:sub(d, d)) |
|||
if c > 0 then sign = '-' end |
|||
while #t < (width or 0) do |
|||
table.insert(t, 1, '0') |
|||
end |
|||
local intPart = table.concat(t, '') |
|||
-- compute the fractional part |
|||
local tf = {} |
|||
while f > 0 and #tf < (precision or 10) do |
|||
f = f * base |
|||
i, f = math.modf(f) |
|||
table.insert(tf, digits:sub(i + 1, i + 1)) |
|||
end |
|||
-- add trailing zeros if needed |
|||
if precision and #tf < precision then |
|||
for i = 1, precision - #tf do |
|||
table.insert(tf, '0') |
|||
end |
|||
end |
|||
-- replace any full-width Unicode characters in the string with their ASCII equivalents |
|||
fracPart = table.concat(tf, '') |
|||
n = normalizeFullWidthChars(n) |
|||
-- remove trailing zeros if not needed |
|||
-- handle scientific notation with whitespace around the 'e' e.g. '5 e7' |
|||
if not precision then |
|||
n = n:gsub('%s*[eE]%s*', 'e') |
|||
end |
|||
from = from or 10 |
|||
local num = tonumber(n, from) |
|||
-- add the radix point if needed |
|||
base = tonumber(base) |
|||
if #fracPart > 0 then |
|||
precision = tonumber(precision) |
|||
fracPart = '.' .. fracPart |
|||
width = tonumber(width) |
|||
end |
|||
if not num or not base then return default or n end |
|||
return (prefix or '') .. sign .. intPart .. fracPart .. (suffix or '') |
|||
local i, f = math.modf(num) |
|||
local t = {} |
|||
repeat |
|||
local d = (i % base) + 1 |
|||
i = math.floor(i / base) |
|||
table.insert(t, 1, digits:sub(d, d)) |
|||
until i == 0 |
|||
while #t < (width or 0) do |
|||
table.insert(t, 1, '0') |
|||
end |
|||
local intPart = table.concat(t, '') |
|||
-- compute the fractional part |
|||
local tf = {} |
|||
while f > 0 and #tf < (precision or 10) do |
|||
f = f * base |
|||
i, f = math.modf(f) |
|||
table.insert(tf, digits:sub(i + 1, i + 1)) |
|||
end |
|||
-- add trailing zeros if needed |
|||
if precision and #tf < precision then |
|||
for i = 1, precision - #tf do |
|||
table.insert(tf, '0') |
|||
end |
|||
end |
|||
fracPart = table.concat(tf, '') |
|||
-- remove trailing zeros if not needed |
|||
if not precision then |
|||
fracPart = fracPart:gsub('0*$', '') |
|||
end |
|||
-- add the radix point if needed |
|||
if #fracPart > 0 then |
|||
fracPart = '.' .. fracPart |
|||
end |
|||
return (prefix or '') .. sign .. intPart .. fracPart .. (suffix or '') |
|||
end |
end |
||
function p.convert(frame) |
function p.convert(frame) |
||
-- Allow for invocation via #invoke or directly from another module |
|||
local args |
|||
if frame == mw.getCurrentFrame() then |
|||
args = frame.args |
|||
else |
|||
args = frame |
|||
end |
|||
local n = args.n |
|||
local base = args.base |
|||
local from = args.from |
|||
local precision = args.precision |
|||
local width = args.width |
|||
local default = args.default |
|||
local prefix = args.prefix |
|||
local suffix = args.suffix |
|||
return _convert(n, base, from, precision, width, default, prefix, suffix) |
|||
end |
end |
||
Line 110: | Line 110: | ||
return function(frame) |
return function(frame) |
||
args = frame.args |
args = frame.args |
||
return _convert(mw.text.trim(args[1]), base, from, args['precision'], args['width'], |
|||
args['default'], args['prefix'], args['suffix']) |
|||
end |
|||
end |
end |
||
}) |
}) |
Revision as of 07:20, 12 April 2022
![]() | This Lua module is used on approximately 37,000 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them. |
Converts numbers to a specified base between 2 and 36, for use in templates such as {{Binary}}, {{Octal}}, {{Hexadecimal}}, etc.
Usage
local BaseConvert = require('Module:BaseConvert')
BaseConvert.convert({n = 14600926, base = 16}) -- returns 'DECADE'
Arguments:
- n - (required) the number to be converted, as a string. It may be a number instead, if the input base is 10.
- base - (required) the base to which the number should be converted. May be between 2 and 36, inclusive.
- from - the base of the input. Defaults to 10 (or 16 if the input has a leading '0x'). Note that bases other than 10 are not supported if the input has a fractional part.
- precision - number of digits to be rendered after the radix point. Trailing zeros will be added if needed. If not specified, however many digits are needed will be shown, up to 10.
- width - minimum number of digits to be rendered before the radix point. Leading zeros will be added if needed.
- default - Value to return if n is empty or non-numeric. Defaults to the value of n.
- prefix / suffix - wikitext to add before/after the returned result. Will not be added if n is empty or non-numeric. For example, you might use a prefix of
0x
when converting to hex, or a suffix of<sub>8</sub>
when converting to octal.
From templates
In wikimarkup, this module may be called with a function name ntom
, e.g.:
Markup | Renders as |
---|---|
{{#invoke:BaseConvert|16to10| FF }} |
255 |
{{#invoke:BaseConvert|10to36|500}} |
DW |
{{#invoke:BaseConvert|10to16|Foo|default=0}} |
0 |
All options above are supported, excluding |base=
, |from=
and |n=
which are set by the mandatory options.
Edge cases
Markup | Renders as |
---|---|
{{#invoke:BaseConvert|10to10|500}} |
500 |
{{#invoke:BaseConvert|10to10|FooBar}} |
FooBar |
{{#invoke:BaseConvert|10to10|FooBar|default=}} |
|
{{#invoke:BaseConvert|10to16|Foo}} |
Foo |
local p = {}
local digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
function normalizeFullWidthChars(s)
return mw.ustring.gsub(s, '[!-~]', function(s)
return mw.ustring.char(mw.ustring.codepoint(s, 1) - 0xFEE0)
end)
end
function _convert(n, base, from, precision, width, default, prefix, suffix)
n = '' .. n -- convert to a string
-- strip off any leading '0x' (unless x is a valid digit in the input base)
from = tonumber(from)
if not from or from < 34 then
local c
n, c = n:gsub('^(-?)0[Xx]', '%1')
if c > 0 and not from then from = 16 end
end
-- check for a negative sign. Do this while the input is still in string form,
-- because tonumber doesn't support negative numbers in non-10 bases.
local sign = ''
local c
n, c = n:gsub('^-', '')
if c > 0 then sign = '-' end
-- replace any full-width Unicode characters in the string with their ASCII equivalents
n = normalizeFullWidthChars(n)
-- handle scientific notation with whitespace around the 'e' e.g. '5 e7'
n = n:gsub('%s*[eE]%s*', 'e')
from = from or 10
local num = tonumber(n, from)
base = tonumber(base)
precision = tonumber(precision)
width = tonumber(width)
if not num or not base then return default or n end
local i, f = math.modf(num)
local t = {}
repeat
local d = (i % base) + 1
i = math.floor(i / base)
table.insert(t, 1, digits:sub(d, d))
until i == 0
while #t < (width or 0) do
table.insert(t, 1, '0')
end
local intPart = table.concat(t, '')
-- compute the fractional part
local tf = {}
while f > 0 and #tf < (precision or 10) do
f = f * base
i, f = math.modf(f)
table.insert(tf, digits:sub(i + 1, i + 1))
end
-- add trailing zeros if needed
if precision and #tf < precision then
for i = 1, precision - #tf do
table.insert(tf, '0')
end
end
fracPart = table.concat(tf, '')
-- remove trailing zeros if not needed
if not precision then
fracPart = fracPart:gsub('0*$', '')
end
-- add the radix point if needed
if #fracPart > 0 then
fracPart = '.' .. fracPart
end
return (prefix or '') .. sign .. intPart .. fracPart .. (suffix or '')
end
function p.convert(frame)
-- Allow for invocation via #invoke or directly from another module
local args
if frame == mw.getCurrentFrame() then
args = frame.args
else
args = frame
end
local n = args.n
local base = args.base
local from = args.from
local precision = args.precision
local width = args.width
local default = args.default
local prefix = args.prefix
local suffix = args.suffix
return _convert(n, base, from, precision, width, default, prefix, suffix)
end
setmetatable(p, {
__index = function(t, k)
from, base = k:match('^([0-9]+)to([0-9]+)$')
if not from then return nil end
return function(frame)
args = frame.args
return _convert(mw.text.trim(args[1]), base, from, args['precision'], args['width'],
args['default'], args['prefix'], args['suffix'])
end
end
})
return p