We have both a floor and a ceil, so why not have a round? It’s used fairly often for user input/ display purposes, and I could see people using floor a lot less where it shouldn’t be used if they didn’t have to make a round function each time.
I know that round() is basically just floor X+0.5, but ceil is also just floor(X+1)
As for it being a Lua thing, Roblox has modified core libraries before, hasn’t it?
The reason there isn’t a math.round already is because there’s half a dozen2 ways to round a number in the way you’re intending:
This is unimportant. You specify a rounding mode (it’s actually easy to specify - you just pick the same rounding mode fp unit uses for rounding extra mantissa bit) and roll with it. C has a round function, GLSL has a round function, HLSL has a round function.
Rounding to specific places is tricky - the result may not be representative as a floating point number (for example, 1.02 isn’t). You want a string conversion for this (as @Anaminus mentions, string.format already can do that)
I’m not sure what specifically your second example intends to achieve (what should happen to 1.149999999? 1.15 is not representable as a float).
I have a different idea of what math.round() would do:
This will take three arguments:
math.round(number, roundToNearestMultiple, decimalPlaces)
-- e.g.
math.round(6.2538, 2.511111, 3) -- prints 5.022
Let me demonstrate what it would do:
-- it would essentially combine these two functions:
function RoundToNearest(x, nearestSnap)
return nearestSnap * math.floor(x / nearestSnap + 0.5)
end
function Round(x, places)
local decimal = math.pow(10, places or 0)
return math.floor(x * decimal + 0.5) / decimal
end
print(RoundToNearest(0, 10)) -- prints 0
print(RoundToNearest(7.535, 13.5234)) -- prints 13.5234
print(Round(10.5234, 2)) -- prints 10.52
print(Round(RoundToNearest(12, 13.498787), 4)) -- prints 13.4988
-- Here's the combined version, exactly how it would look if implemented
function RoundToNearestWithDecimalAmount(x, nearestSnap, decimalPlaces)
assert(x ~= nil, " argument 1 missing or nil")
assert(nearestSnap ~= nil or decimalPlaces ~= nil, " math.round requires at least 2 arguments")
local thisSnap;
if nearestSnap then
thisSnap = nearestSnap * math.floor(x / nearestSnap + 0.5)
if not decimalPlaces then
return thisSnap
end
end
assert(decimalPlaces >= 0, " decimal places can only be positive")
assert(decimalPlaces % 1 == 0, " decimal places has to be an integer")
local thisDecimal = math.min(math.pow(10, decimalPlaces or 0), 1e9) -- so it doesn't crash and print "nan"
local roundedSnap = math.floor((thisSnap or x) * thisDecimal + 0.5) / thisDecimal
return roundedSnap
end
print(RoundToNearestWithDecimalAmount(100, 90)) -- prints 90
print(RoundToNearestWithDecimalAmount(8, 2, nil)) -- prints 8
print(RoundToNearestWithDecimalAmount(12.123123, 3.1, 2)) -- prints 12.4
print(RoundToNearestWithDecimalAmount(12.1234, nil, 3)) -- prints 12.123