Lua runner with rich builtin stdlib
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
a/lua/stdlib/math.lua

115 lines
3.7 KiB

-- Round a finite float half away from zero, exactly.
-- math.modf is exact, unlike the naive floor(x + 0.5) which goes wrong
-- for values like 0.49999999999999994 (x + 0.5 rounds up to 1.0 in the float domain).
local function roundHalfAwayFromZero(x)
local i, f = math.modf(x)
if f >= 0.5 then
return i + 1
elseif f <= -0.5 then
return i - 1
end
return i
end
--- Round a number to the nearest integer, or to N decimal places.
--- Rounds half away from zero. round(value, 0) returns an integer; round(value, places > 0) returns a float.
function math.round(value, places)
if type(value) ~= "number" then
error("round() called with a non-number value")
end
if places == nil then
places = 0
else
places = math.tointeger(places)
if places == nil then
error("round() places must be an integer number")
end
if places < 0 then
error("round() called with negative places")
end
end
if math.type(value) == "integer" then
if places == 0 then
return value
end
return value + 0.0
end
if not math.isFinite(value) then
error("round() called with a non-finite value (NaN or infinity)")
end
if places == 0 then
local int = math.tointeger(roundHalfAwayFromZero(value))
if int == nil then
error("round() called with value out of range of Lua integer")
end
return int
end
local mul = 10.0 ^ places
local scaled = value * mul
if not math.isFinite(scaled) then
return value
end
return roundHalfAwayFromZero(scaled) / mul
end
--- Clamp a number to an interval [min, max]. Either bound may be nil (half-open).
function math.clamp(value, min, max)
if type(value) ~= "number" then
error("Non-number passed to clamp() as value")
end
if min ~= nil and type(min) ~= "number" then
error("Non-number passed to clamp() as minimum")
end
if max ~= nil and type(max) ~= "number" then
error("Non-number passed to clamp() as maximum")
end
if min ~= nil and max ~= nil and min > max then
error("clamp() minimum is greater than maximum")
end
if min ~= nil and value < min then return min end
if max ~= nil and value > max then return max end
return value
end
--- Return true if value is a finite number (not NaN or infinity).
function math.isFinite(value)
if type(value) ~= "number" then return false end
return value == value and value ~= math.huge and value ~= -math.huge
end
--- Return the sign of a number: -1, 0, or 1. NaN yields 0.
function math.sign(value)
if type(value) ~= "number" then
error("Non-number passed to sign()")
end
if value > 0 then return 1
elseif value < 0 then return -1
else return 0
end
end
--- Scale a value linearly from [inMin, inMax] to [outMin, outMax].
--- If clamp is true, the result is clamped to the output range.
function math.scale(value, inMin, inMax, outMin, outMax, clamp)
if type(value) ~= "number" then error("Non-number passed to scale() as value") end
if type(inMin) ~= "number" or type(inMax) ~= "number" then error("Non-number passed to scale() as input range") end
if type(outMin) ~= "number" or type(outMax) ~= "number" then error("Non-number passed to scale() as output range") end
if inMin == inMax then error("Input range cannot be zero (inMin == inMax)") end
local ratio = (value - inMin) / (inMax - inMin)
local result = outMin + ratio * (outMax - outMin)
if clamp then
local lo, hi = outMin, outMax
if lo > hi then lo, hi = hi, lo end
if result < lo then return lo
elseif result > hi then return hi
end
end
return result
end