Can this simple rounding function be more efficient?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
togFox
Party member
Posts: 828
Joined: Sat Jan 30, 2021 9:46 am
Location: Brisbane, Oztralia

Can this simple rounding function be more efficient?

Post 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.
Last project:
https://togfox.itch.io/hwarang
A card game that brings sword fighting to life.
Current project:
Turn-based PBEM horse stable (racing) management sim: https://togfox.itch.io/horse-stable-manager
https://discord.gg/HeHgwE5nsZ
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Can this simple rounding function be more efficient?

Post 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
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
Jasoco
Inner party member
Posts: 3726
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Can this simple rounding function be more efficient?

Post 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
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Can this simple rounding function be more efficient?

Post 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.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
Bigfoot71
Party member
Posts: 287
Joined: Fri Mar 11, 2022 11:07 am

Re: Can this simple rounding function be more efficient?

Post 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 :)
My avatar code for the curious :D V1, V2, V3.
User avatar
togFox
Party member
Posts: 828
Joined: Sat Jan 30, 2021 9:46 am
Location: Brisbane, Oztralia

Re: Can this simple rounding function be more efficient?

Post 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. :)
Last project:
https://togfox.itch.io/hwarang
A card game that brings sword fighting to life.
Current project:
Turn-based PBEM horse stable (racing) management sim: https://togfox.itch.io/horse-stable-manager
https://discord.gg/HeHgwE5nsZ
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 3 guests