If your Lua uses double precision IEC-559 (aka IEEE-754) floats, as most do, and your numbers are relatively small (the method is guaranteed to work for inputs between -251 and 251), the following efficient code will perform rounding using your FPU's current rounding mode, which is usually round to nearest, ties to even:
local function round(num)
return num + (2^52 + 2^51) - (2^52 + 2^51)
end
(Note that the numbers in parentheses are calculated at compilation time; they don't affect runtime).
For example, when the FPU is set to round to nearest or even, this unit test prints "All tests passed":
local function testnum(num, expected)
if round(num) ~= expected then
error(("Failure rounding %.17g, expected %.17g, actual %.17g")
:format(num+0, expected+0, round(num)+0))
end
end
local function test(num, expected)
testnum(num, expected)
testnum(-num, -expected)
end
test(0, 0)
test(0.2, 0)
test(0.4, 0)
-- Most rounding algorithms you find on the net, including Ola M's answer,
-- fail this one:
test(0.49999999999999994, 0)
-- Ties are rounded to the nearest even number, rather than always up:
test(0.5, 0)
test(0.5000000000000001, 1)
test(1.4999999999999998, 1)
test(1.5, 2)
test(2.5, 2)
test(3.5, 4)
test(2^51-0.5, 2^51)
test(2^51-0.75, 2^51-1)
test(2^51-1.25, 2^51-1)
test(2^51-1.5, 2^51-2)
-- Some of these will fail with the function above,
-- but will pass with the function below.
--test(2^51 + 1, 2^51 + 1)
--test(2^51 + 1.5, 2^51 + 2)
--test(2^51 + 2, 2^51 + 2)
--test(2^51 + 2.5, 2^51 + 2)
--test(2^51 + 3, 2^51 + 3)
--test(2^51 + 3.5, 2^51 + 4)
--test(2^52 + 1, 2^52 + 1)
--test(2^52 + 2, 2^52 + 2)
--test(2^53 + 2, 2^53 + 2)
print("All tests passed")
Here's another (less efficient, of course) algorithm that performs the same FPU rounding but works for all numbers:
local function round(num)
if math.abs(num) > 2^52 then
return num
end
return num < 0 and num - 2^52 + 2^52 or num + 2^52 - 2^52
end