math.round has problems or bug?

Hi guys!

I came across something very strange that did not happen to me.

if I do an operation inside math.round with numbers near 0 this returns me “-1.#IND”.

if instead I calculate after using round I get 0 (which is right)

To explain myself better, I have extrapolated the code. Leaving aside the use that I do try first the function test1 (everything goes according to plan) and test2 (error).

as you can see from test1 to test2 change ONLY the function foo:

fisrt:

 local dx, dy = x-var1, y-var2 local ccgm = staticVal / sqrt(dx^2 + dy^2) dx, dy = round(dx), round(dy)

second:

local dx, dy = round(x-var1), round(y-var2)

below all the code to perform the tests(call test1 first and look at the console when right reaches 0 and then do the same thing with test2):

local function test1() local static1 = 140 local static2 = 300 local sqrt = math.sqrt local round = math.round local staticVal = 15 local var1=180 local var2=300 local function foo (x, y) local dx, dy = x-var1, y-var2 local ccgm = staticVal / sqrt(dx^2 + dy^2) dx, dy = round(dx), round(dy) print(dx,dy) return dx\*ccgm, dy\*ccgm end local now = system.getTimer( ) timer.performWithDelay(30, function(event) local time = event.time local dt = (time - now) / 1000 now = time local vx,vy = foo(static1, static2) var1 = (var1+(dt\*vx)) end,-1) end local function test2() local static1 = 140 local static2 = 300 local sqrt = math.sqrt local round = math.round local staticVal = 15 local var1=180 local var2=300 local function foo2(x, y) local dx, dy = round(x-var1), round(y-var2) --local dx dy = x-var1, y-var2 local ccgm = staticVal / sqrt(dx^2 + dy^2) print(dx,dy) return dx\*ccgm, dy\*ccgm end local now = system.getTimer( ) timer.performWithDelay(30, function(event) local time = event.time local dt = (time - now) / 1000 now = time local vx,vy = foo2(static1, static2) var1 = (var1+(dt\*vx)) end,-1) end --test1() --test2()

The problem is you are dividing by zero to get ccgm.
 
You need to check to be sure that your square root calculation does not round down to zero.

local test1 local test2 local startsAt = 141 local runs = 4 test1 = function() local static1 = 140 local static2 = 300 local sqrt = math.sqrt local round = math.round local staticVal = 15 local var1=startsAt local var2=300 local count = 0 local function foo (x, y) count = count + 1 local dx, dy = x-var1, y-var2 local ccgm = staticVal / sqrt(dx^2 + dy^2) dx, dy = round(dx), round(dy) print(string.format("TEST 1 foo() count: %d dx: %f dy: %f ccgm: %f staticVal: %f sqrt: %f", count, dx,dy,ccgm, staticVal, sqrt(dx^2 + dy^2))) return dx\*ccgm, dy\*ccgm end local now = system.getTimer( ) timer.performWithDelay(30, function(event) local time = event.time local dt = (time - now) / 1000 now = time local vx,vy = foo(static1, static2) print(string.format("TEST 1 count: %d dt: %f vx: %f\n------------------", count, dt, vx)) var1 = (var1+(dt\*vx)) if( count == runs ) then test2() end end,runs) end test2 = function() local static1 = 140 local static2 = 300 local count = 0 local sqrt = math.sqrt local round = math.round local staticVal = 15 local var1=startsAt local var2=300 local function foo2(x, y) count = count + 1 local dx, dy = round(x-var1), round(y-var2) --local dx dy = x-var1, y-var2 local ccgm = staticVal / sqrt(dx^2 + dy^2) --print("TEST 2", count, dx,dy) print(string.format("TEST 2 foo2() count: %d dx: %f dy: %f ccgm: %f staticVal: %f sqrt: %f", count, dx,dy,ccgm, staticVal, sqrt(dx^2 + dy^2))) return dx\*ccgm, dy\*ccgm end local now = system.getTimer( ) timer.performWithDelay(30, function(event) local time = event.time local dt = (time - now) / 1000 now = time local vx,vy = foo2(static1, static2) print(string.format("TEST 2 count: %d dt: %f vx: %f\n------------------", count, dt, vx)) var1 = (var1+(dt\*vx)) end,runs) end --table.dump(math) test1()

This output line is the hint:

TEST 2 foo2() count: 2 dx: 0.000000 dy: 0.000000 ccgm: 1.#INF00 staticVal: 15.000000 sqrt: 0.000000

You need to add code to check for the special case of a zero-length segment and handle it differently.

Thanks for fast response!

So this is normal behavior? is it better not to perform operations inside mathematical functions?

I not sure I understand your follow on question, but if you’re asking:

“Is it normal for square root calculations to return 0?”

The answer is, “Yes, if the value you are taking the square root is VERY small or zero.”

Also, it is better not to do anything that might result in a divide by zero.  So, in this case doing that calculation in the call to sqrt() is a bad idea as it may result in a return value of 0.

I did not explain well.

My question is:

Why if I do(using the same data):

local dx, dy = x-var1, y-var2 dx, dy = round(dx), round(dy) print(dx)

when “x-var1” is equal to “0”, “dx” takes “0”

while if I do

local dx, dy = round(x-var1), round(y-var2) print(dx)

when “x-var1” is equal to “0”, “dx” takes “-1.#IND

it seems that passing as a parameter to “math.round()” operations does not work as well as passing finite values

p.s. (Try the tests attached in the above code (first post) to confirm)

You’re problem is not the rounding but, as roaming already told, your division by 0 which is kind of not defined but the result is ccgm become infinite which, as a result changes the results from your foo function and then var1 which is used in the next iteration of foo.

 

You can verify this f.i. by adding the following lines after your local ccgm = … line of code

 

[lua]

if ((dx^2) + (dy^2)) < 0.00000000000000000001 then

    ccgm = 0.00000000000000000001

end

[/lua]

  1. yes.  2.  that’s not the issue

your two functions are not numerically equivalent, so i won’t bother running the test:  the first rounds AFTER the sqrt-division, the second rounds BEFORE the sqrt-division.  so no surprise the results differ.  your question is equivalent to asking:

dx = 1e-12 ccgm = 1 / dx -- no problem, ccgm now == 1e12 dx = math.round(dx) -- versus dx = math.round(1e-12) -- dx now equals zero ccgm = 1 / dx -- division by zero, ccgm now undefined (+infinity per ieee754, ie ccgm == math.huge)

you’re on the fringe of precision here, try printing all 15 digits of your sqrt result just prior to division to see the difference.

@dave

You’re wrong here as the rounding, within the scope of the function, is actually done on the same and unchanged variable/value (dx and dy) in both cases. You can just move the print/rounding code above the sqrt and still get the same problem.

The core is of course the division by 0 but only because it is feed back somehow into later iterations of foo.

i can accept that, as i didn’t run the code, still same problem only later

Ok grazie ragazzi!

Ho seguito tutti e ho capito.

Il problema era con le iterazioni successive non con la funzione “math.round”.

@davebollinger 

no later does not occur, because the rounding is always after to the square root

 
 
I agree w/ everything Michael said, except I want to give you one word of caution.  The value: 0.00000000000000000001 may be too small for some CPUs in some devices.  It will round to 0 or some weird value (not sure on this).
 
My suggestion is a modified version of Michael’s solution.
 

-- Set this in main.lua before executing any of your game code math.tiny = math.tiny or 1e-6 -- safer in case math.tiny ever becomes part of lib -- Now you can use it anywhere if ((dx^2) + (dy^2)) \<= math.tiny then ccgm = math.tiny end

FYI, there is already a math.huge as part of the math.* library

PS - My choice for math.tiny isn’t really that tiny, but it is definitely small enough for any game calculation I can think of.

@maximo,

I think everyone here will agree, this has nothing to do with math.round()nor inline calculations.

The problem is a division by zero which you are not checking for.

Once more, the problem is this line,

local ccgm = staticVal / sqrt(dx^2 + dy^2)

specifically, this calculation,

sqrt(dx^2 + dy^2)

which produces the zero in the division-by-zero.

Absolutely, I didn’t meant this to be a solution but just a quick way to show where the actual error came from without thinking the actual reason behind the code.

You’d also probably do the tiny check before squaring the values as tiny^2 will be even smaller and then set ccgm to actually a very big value as whatever divided by a tiny value results in a very big one. But maximo is the one who knows best what the code is intended to do and how to adjust it.

@roaminggamer

Yes, the basic problem is that because if I divide by 0 then call the function foo dx already has an invalid value.

For how I had written the code I took a while to understand it but thanks to you I have to go

I do this…

local newValue = 1 / mclamp(myValueThatMayBeZero, 0.00000000000001, myValueThatMayBeZero)

@sgs

That would not work with negative values.

If you know you only need/want positives, using max instead of clamp would be the better solution as clamp will do an upper bounds check never required.

The problem is you are dividing by zero to get ccgm.
 
You need to check to be sure that your square root calculation does not round down to zero.

local test1 local test2 local startsAt = 141 local runs = 4 test1 = function() local static1 = 140 local static2 = 300 local sqrt = math.sqrt local round = math.round local staticVal = 15 local var1=startsAt local var2=300 local count = 0 local function foo (x, y) count = count + 1 local dx, dy = x-var1, y-var2 local ccgm = staticVal / sqrt(dx^2 + dy^2) dx, dy = round(dx), round(dy) print(string.format("TEST 1 foo() count: %d dx: %f dy: %f ccgm: %f staticVal: %f sqrt: %f", count, dx,dy,ccgm, staticVal, sqrt(dx^2 + dy^2))) return dx\*ccgm, dy\*ccgm end local now = system.getTimer( ) timer.performWithDelay(30, function(event) local time = event.time local dt = (time - now) / 1000 now = time local vx,vy = foo(static1, static2) print(string.format("TEST 1 count: %d dt: %f vx: %f\n------------------", count, dt, vx)) var1 = (var1+(dt\*vx)) if( count == runs ) then test2() end end,runs) end test2 = function() local static1 = 140 local static2 = 300 local count = 0 local sqrt = math.sqrt local round = math.round local staticVal = 15 local var1=startsAt local var2=300 local function foo2(x, y) count = count + 1 local dx, dy = round(x-var1), round(y-var2) --local dx dy = x-var1, y-var2 local ccgm = staticVal / sqrt(dx^2 + dy^2) --print("TEST 2", count, dx,dy) print(string.format("TEST 2 foo2() count: %d dx: %f dy: %f ccgm: %f staticVal: %f sqrt: %f", count, dx,dy,ccgm, staticVal, sqrt(dx^2 + dy^2))) return dx\*ccgm, dy\*ccgm end local now = system.getTimer( ) timer.performWithDelay(30, function(event) local time = event.time local dt = (time - now) / 1000 now = time local vx,vy = foo2(static1, static2) print(string.format("TEST 2 count: %d dt: %f vx: %f\n------------------", count, dt, vx)) var1 = (var1+(dt\*vx)) end,runs) end --table.dump(math) test1()

This output line is the hint:

TEST 2 foo2() count: 2 dx: 0.000000 dy: 0.000000 ccgm: 1.#INF00 staticVal: 15.000000 sqrt: 0.000000

You need to add code to check for the special case of a zero-length segment and handle it differently.

Thanks for fast response!

So this is normal behavior? is it better not to perform operations inside mathematical functions?