Округление до шага
Часто требуется математическое действие, округляющее определенное значение к заданному шагу. Такого рода действия необходимы при написании торговых роботов, чтобы в частности привести значение к шагу цены инструмента или округлить текущее время к границе таймфрейма. Например, необходимо округлить число 9876.54321 до шага 0.01. Результат будет 9876.54. Действие тривиальное, но "больная голова рукам покоя не даёт. ...
Все ниже сказанное относится к округлению "вниз" (вариант floor). Вариант ceil аналогичен.
Везде встречается следующее решение для языка lua:
function floor_to_step(what,step) return math.floor(what/step) * step end
Оно замечательно работает. Собственно я его всегда и использовал. Но ведь задачу можно решить и иначе, верно?
function floor_to_step(what,step) return what - what % step return
Выглядит проще на вызов функции из библиотеки (а это большие накладные расходы на фоне двух других арифметических действий). Давайте напишем тест и проверим.
local b
local s1,s2
s1 = os.clock()
for i=1,100000000 do
b = i - (i%87.56565656)
end
s1 = os.clock() - s1
s2 = os.clock()
for i=1,100000000 do
b = math.floor(i/87.565656) * 87.565656
end
s2 = os.clock() - s2
print(tostring(s1) .. " " .. tostring(s2))
Округляем до произвольно взятого (с потолка) шага 87.56565656 сто миллионов чисел первым и вторым способом и замеряем время выполнения.
Результат выполнения на моем ноутбуке таков:
5.796 18.528
Что мы получаем? Способ с использованием вызова библиотечной функции проигрывает способу с вычитаем остатка от деления в 3 с лишним раза.
В принципе такой результат можно было предвидеть: дополнительное время тратится на вызов библиотечной функции, да и набор действий сложнее (деление и умножение занимают больше процессорного времени, чем вычитание и взятие остатка).
Осталось удостовериться, что оба метода дают одинаковый результат в числах ( у нас все же вычисления идут в арифметике плавающей точки). Просто удостоверимся, написав простейший тест.
local x,y,z1,z2
for i = -10000000,1000000 do
x = math.random(1,100000)
y = math.random(1,100000)
z1 = x - x%y
z2 = math.floor(x/y) * y
if z1 ~= z2 then
print(tostring(z1) .. " " .. tostring(z2))
end
end
Если хотя бы один раз результаты не сойдутся, мы получим на экране сообщение об этом.
Запускаем - и тишина. Расхождений нет. Да здравствуем мы все!
Конечно, такого рода различия имеют смысл лишь в случае, когда округление до шага используется в программе постоянно. Разовое вычисление никак не повлияет на производительность скрипта или программы.
См. также О пользе inline-кода или библиотека bit
В третьем листинге если math.floor вынести в локальную переменную вне цикла, то преимущество сократиться с 3 до 2.
...и еще... никак не соображу, как же написать функцию для округления вверх.
return what - what % step
return
Два раза return?
Конечно это опечатка. Вместо второго return должен быть end
RSS лента комментариев этой записи