Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

math.round has problems or bug?
Started by maximo97 May 08 2018 03:15 PM

- - - - -
16 replies to this topic
math.round

Best Answer roaminggamer , 09 May 2018 - 07:23 AM

@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.

[TOPIC CONTROLS]
This topic has been archived. This means that you cannot reply to this topic.
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

maximo97

[GLOBAL: userInfoPane.html]
maximo97
  • Contributor

  • 232 posts
  • Corona SDK

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()


[TOPIC: post.html]
#2

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,641 posts
  • Corona SDK

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


[TOPIC: post.html]
#3

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,641 posts
  • Corona SDK

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



[TOPIC: post.html]
#4

maximo97

[GLOBAL: userInfoPane.html]
maximo97
  • Contributor

  • 232 posts
  • Corona SDK

Thanks for fast response!

 

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



[TOPIC: post.html]
#5

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,641 posts
  • Corona SDK

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.



[TOPIC: post.html]
#6

maximo97

[GLOBAL: userInfoPane.html]
maximo97
  • Contributor

  • 232 posts
  • Corona SDK

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)


Edited by maximo97, 09 May 2018 - 12:55 AM.


[TOPIC: post.html]
#7

Michael Flad

[GLOBAL: userInfoPane.html]
Michael Flad
  • Contributor

  • 267 posts
  • Corona SDK

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
 
if ((dx^2) + (dy^2)) < 0.00000000000000000001 then
    ccgm = 0.00000000000000000001
end


[TOPIC: post.html]
#8

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Corona Geek

  • 1,373 posts
  • Corona SDK

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

 

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.



[TOPIC: post.html]
#9

Michael Flad

[GLOBAL: userInfoPane.html]
Michael Flad
  • Contributor

  • 267 posts
  • Corona SDK

@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.



[TOPIC: post.html]
#10

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Corona Geek

  • 1,373 posts
  • Corona SDK

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



[TOPIC: post.html]
#11

maximo97

[GLOBAL: userInfoPane.html]
maximo97
  • Contributor

  • 232 posts
  • Corona SDK

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


Edited by maximo97, 09 May 2018 - 06:14 AM.


[TOPIC: post.html]
#12

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,641 posts
  • Corona SDK

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
 

if ((dx^2) + (dy^2)) < 0.00000000000000000001 then
    ccgm = 0.00000000000000000001
end

 
 
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.


Edited by roaminggamer, 09 May 2018 - 07:48 AM.


[TOPIC: post.html]
#13

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,641 posts
  • Corona SDK

  Best Answer

@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.



[TOPIC: post.html]
#14

Michael Flad

[GLOBAL: userInfoPane.html]
Michael Flad
  • Contributor

  • 267 posts
  • Corona SDK

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.



[TOPIC: post.html]
#15

maximo97

[GLOBAL: userInfoPane.html]
maximo97
  • Contributor

  • 232 posts
  • Corona SDK

@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



[TOPIC: post.html]
#16

SGS

[GLOBAL: userInfoPane.html]
SGS
  • Corona Geek

  • 2,194 posts
  • Corona SDK

I do this...

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


[TOPIC: post.html]
#17

Michael Flad

[GLOBAL: userInfoPane.html]
Michael Flad
  • Contributor

  • 267 posts
  • Corona SDK

@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.




[topic_controls]
[/topic_controls]