如果你的 Lua 使用双精度 IEC-559(也称为 IEEE-754)浮点数,大多数情况下都是如此,并且你的数字相对较小(该方法保证可用于 -2
51 到 2
51 之间的输入),则以下高效代码将使用你的 FPU 当前的舍入模式执行舍入,通常是四舍五入到最近的偶数:
local function round(num)
return num + (2^52 + 2^51) - (2^52 + 2^51)
end
注意,括号中的数字是在编译时计算的,不影响运行时。
例如,当FPU设置为四舍五入到最近偶数时,该单元测试将打印“所有测试都通过”:
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)
print("All tests passed")
这里有另一种算法(当然不够高效),它可以对所有数字执行相同的FPU舍入:
local function round(num)
local ofs = 2^52
if math.abs(num) > ofs then
return num
end
return num < 0 and num - ofs + ofs or num + ofs - ofs
end