Page 1 of 1

Can this simple rounding function be more efficient?

Posted: Mon Dec 12, 2022 4:46 am
by togFox
I've been told that operating on a string and then converting to number is inefficient and yeah - I get it - but I can't think of a fast way to do this and still be robust:

Code: Select all

function round(num, idp)
	--Input: number to round; decimal places required
	assert(num ~= nil, "Can't ROUND a nil value")
	return tonumber(string.format("%." .. (idp or 0) .. "f", num))
end
Do you guys have a faster rounding function? I'm normally blasé about fractions of seconds but I use this to round thousands of rows in a data-set so performance matters in this case.

Thanks.

Re: Can this simple rounding function be more efficient?

Posted: Mon Dec 12, 2022 6:21 am
by zorg
why use string functions when this kind of thing is perfectly doable with math?

Code: Select all

function truncateToDecimal(num, dec) -- assume num is a number by default, assume dec is a number or nil by default
  dec = dec or 0 -- default value
  return num > 0 and math.floor(num * 10^dec)/10^dec or math.ceil(num * 10^dec)/10^dec -- works for both positives and negatives
end
Truncation simpler than rounding btw; if you want to have the dec-1th value affect the end, that'll be an excercise left to the reader. :3

Re: Can this simple rounding function be more efficient?

Posted: Mon Dec 12, 2022 1:40 pm
by Jasoco
I was using this code below, which on second glance is pretty much exactly the same as zorg's code formatted differently.

Code: Select all

function math_round(val, decimal)
	if not val then return 0 end
	if (decimal) then
		return math.floor( (val * 10^decimal) + 0.5) / (10^decimal)
	else
		return math.floor(val+0.5)
	end
end

Re: Can this simple rounding function be more efficient?

Posted: Mon Dec 12, 2022 4:08 pm
by zorg
not exactly; your special-case of decimal not specified does a simpler branch, and the +.5 thing is in fact the rounding i left out.

Re: Can this simple rounding function be more efficient?

Posted: Wed Dec 14, 2022 3:22 am
by Bigfoot71
I did some tests and it seems to be the fastest one (tests carried out on the phone, to try again on your side):

Code: Select all

function round(n, mult)
  mult = mult or 1
  return math.floor((n+mult/2)/mult) * mult
end
I found it here: http://lua-users.org/wiki/SimpleRound

My script for comparison tests :

Code: Select all

local n = -.4

function round_1(num, idp)
	--Input: number to round; decimal places required
	assert(num ~= nil, "Can't ROUND a nil value")
	return tonumber(string.format("%." .. (idp or 0) .. "f", num))
end

function round_2(num, dec) -- assume num is a number by default, assume dec is a number or nil by default
  dec = dec or 0 -- default value
  return num > 0 and math.floor(num * 10^dec)/10^dec or math.ceil(num * 10^dec)/10^dec -- works for both positives and negatives
end

function round(n, mult)
  mult = mult or 1
  return math.floor((n+mult/2)/mult) * mult
end

local timeTest = function(f, ...)
  local t1, r, t2 = os.clock(), f(...), os.clock()
  print(string.format("The function took %0.6f seconds to run", t2 - t1))
  return r
end

print("\nOriginal function:\n")

for i = 1, 10 do
  timeTest(round_1, n)
end

print("\nZorg's function:\n")

for i = 1, 10 do
  timeTest(round_2, n)
end

print("\nLast function:\n")

for i = 1, 10 do
  timeTest(round, n)
end
My results :
Image
Image

I always came across the same thing in terms of results, it's up to you to see if it suits you or not now :)

Re: Can this simple rounding function be more efficient?

Posted: Wed Dec 14, 2022 4:57 am
by togFox
This is great guys. As I said, I rarely care about micro-seconds but on this one application - micro-seconds * thousands of datasets = performance gains. :)