Module:Math: Difference between revisions

Jump to navigation Jump to search
Content added Content deleted
en>Dragons flight
m (fmt)
en>Dragons flight
(adds expression checking, more error messages, and better handling of some precision format cases, and precision flooding check)
Line 1: Line 1:
local z = {}
local z = {}
require( "mw.language" );

-- Clean numeric value
function z._cleanNumber( frame, number_string )
-- Attempt basic conversion
local number = tonumber( number_string )
-- If failed, attempt to evaluate input as an expression
if number == nil then
local attempt = frame:preprocess( '{{#expr: ' .. number_string .. '}}' );
attempt = tonumber( attempt );
if attempt ~= nil then
number = attempt;
number_string = tostring( number );
else
number = nil;
number_string = nil;
end
else
-- String is valid but may contain padding, clean it.
number_string = number_string:match( "^%s*(.-)%s*$" );
end
return number, number_string;
end


-- Generate random number
-- Generate random number
Line 17: Line 42:
-- Determine order of magnitude
-- Determine order of magnitude
function z.order(frame)
function z.order(frame)
return z._order(tonumber(frame.args[1] or frame.args.x or 0))
local input_string = (frame.args[1] or frame.args.x or '0');
local input_number;
input_number = z._cleanNumber( frame, input_string );
if input_number == nil then
return '<strong class="error">Formatting error: Order of magnitude input appears non-numeric</strong>'
else
return z._order( input_number )
end
end
end
function z._order(x)
function z._order(x)
Line 26: Line 59:
-- Determines precision of a number using the string representation
-- Determines precision of a number using the string representation
function z.precision( frame )
function z.precision( frame )
return z._precision( frame.args[1] or frame.args.x or '0' )
local input_string = (frame.args[1] or frame.args.x or '0');
local input_number;
input_number, input_string = z._cleanNumber( frame, input_string );
if input_string == nil then
return '<strong class="error">Formatting error: Precision input appears non-numeric</strong>'
else
return z._precision( input_string )
end
end
end
function z._precision( x )
function z._precision( x )
x = string.upper( x )
x = string.upper( x )

-- Remove leading / trailing whitespace
x = x:match "^%s*(.-)%s*$";


local decimal = string.find( x, '.', 1, true )
local decimal = string.find( x, '.', 1, true )
Line 66: Line 104:
return ''
return ''
end
end
local max_value = tonumber( frame.args[1] )
local max_value = nil;
local i = 2;
local i = 1;
while frame.args[i] ~= nil do
while frame.args[i] ~= nil do
local val = tonumber( frame.args[i] );
local val = z._cleanNumber( frame, frame.args[i] );
if val ~= nil then
if val ~= nil then
if val > max_value then
if max_value == nil or val > max_value then
max_value = val;
max_value = val;
end
end
Line 87: Line 125:
return ''
return ''
end
end
local min_value = tonumber( frame.args[1] )
local min_value = nil;
local i = 2;
local i = 1;
while frame.args[i] ~= nil do
while frame.args[i] ~= nil do
local val = tonumber( frame.args[i] );
local val = z._cleanNumber( frame, frame.args[i] );
if val ~= nil then
if val ~= nil then
if val < min_value then
if min_value == nil or val < min_value then
min_value = val;
min_value = val;
end
end
Line 105: Line 143:
-- Rounds a number to specified precision
-- Rounds a number to specified precision
function z.round(frame)
function z.round(frame)
local value = tonumber(frame.args[1] or frame.args.value or 0);
local value, precision;
local precision = tonumber(frame.args[2] or frame.args.precision or 0);
value = z._cleanNumber( frame, frame.args[1] or frame.args.value or 0 );
return z._round( value, precision );
precision = z._cleanNumber( frame, frame.args[2] or frame.args.precision or 0 );
if value == nil or precision == nil then
return '<strong class="error">Formatting error: Round input appears non-numeric</strong>'
else
return z._round( value, precision );
end
end
end
function z._round( value, precision )
function z._round( value, precision )
Line 120: Line 165:
local lang = mw.getContentLanguage();
local lang = mw.getContentLanguage();
local value = tonumber( frame.args[1] or 0 );
local value_string, value, precision;
local precision = tonumber( frame.args[2] or 0 );
value, value_string = z._cleanNumber( frame, frame.args[1] or 0 );
precision = z._cleanNumber( frame, frame.args[2] or 0 );
-- Check for non-numeric input
-- Check for non-numeric input
Line 135: Line 181:
-- some circumstances because the terminal digits will be inaccurately reported.
-- some circumstances because the terminal digits will be inaccurately reported.
if order + precision >= 14 then
if order + precision >= 14 then
precision = 13 - order;
orig_precision = z._precision( value_string );
if order + orig_precision >= 14 then
precision = 13 - order;
end
end
end


Line 165: Line 214:
formatted_num = sign .. formatted_num;
formatted_num = sign .. formatted_num;
-- Pad with zeros, if needed
-- Pad with zeros, if needed
if current_precision < precision then
if current_precision < precision then
local padding;
if current_precision <= 0 then
if current_precision <= 0 then
if precision > 0 then
if precision > 0 then
local zero_sep = lang:formatNum( 1.1 );
local zero_sep = lang:formatNum( 1.1 );
formatted_num = formatted_num .. zero_sep:sub(2,2);
formatted_num = formatted_num .. zero_sep:sub(2,2);

formatted_num = formatted_num .. string.rep( '0', precision );
padding = precision;
if padding > 20 then
padding = 20;
end
formatted_num = formatted_num .. string.rep( '0', padding );
end
end
else
else
formatted_num = formatted_num .. string.rep( '0', precision - current_precision );
padding = precision - current_precision
if padding > 20 then
padding = 20;
end
formatted_num = formatted_num .. string.rep( '0', padding );
end
end
end
end