math.sin / math.cos -- Corona SDK glitch?

I was getting weird physics responses when using math.sin and math.cos at 90, 180 and 270 degrees.

I scoured my code and found that it was the actual math.sin and math.cos methods that were causing the problem.  Take a look at the following code where you would expect the sin = 1 and the cos = 0.

[lua] --converting 90 degrees to radians print( math.rad( 90 ) ) -- 1.5707963267949 --convert radians (raw data from math.rad) into sin and cos values; expect sin = 1, cos = 0 print( math.sin(1.5707963267949) ) -- 1 print( math.cos(1.5707963267949) ) -- -3.4914813388431e-15 --convert radians (rounded) into sin and cos values; expect sin = 1, cos = 0 print( math.sin(1.5707) ) -- 0.99999999536057 print( math.cos(1.5707) ) -- 9.6326794747667e-05 --convert radians (rounded even more) into sin and cos values; expect sin = 1, cos = 0 -- \*close enough print( math.sin(1.57) ) -- 0.99999968293183 print( math.cos(1.57) ) -- 0.00079632671073326 [/lua]

my results are screwy like this at 90, 180 and 270 degrees but seem to work perfectly everywhere else.  Am I missing something or is there a math.sin / math.cos glitch in Corona?

Jonathan

You didn’t tell us which version of Corona SDK you’re using.  That’s critical for debugging:

The latest daily build as I write this is:  2015.2625

I’ve confirmed similar results for 2015.2610, using this variation on your test.

local function round(val, n) if (n) then return math.floor( (val \* 10^n) + 0.5) / (10^n) else return math.floor(val+0.5) end end function string:rpad(len, char) local theStr = self if char == nil then char = ' ' end return theStr .. string.rep(char, len - #theStr) end --converting 90 degrees to radians local radOut = math.rad( 90 ) print( "math.rad( 90 ) ", radOut) for i = 13, 2, -1 do local tmp = round(radOut, i) print( i, " ----------------------------------------------------- ") print( string.rpad("math.sin(" .. tmp .. ")", 30, "." ), math.sin(tmp) ) print( string.rpad("math.cos(" .. tmp .. ")", 30, "." ), math.cos(tmp) ) end

This produced this output running in the Windows Simulator (2015.2610) on a Windows 7 machine.

math.rad( 90 ) 1.5707963267949 13 ----------------------------------------------------- math.sin(1.5707963267949)..... 1 math.cos(1.5707963267949)..... -3.4914813388431e-015 12 ----------------------------------------------------- math.sin(1.570796326795)...... 1 math.cos(1.570796326795)...... -1.0341155355511e-013 11 ----------------------------------------------------- math.sin(1.57079632679)....... 1 math.cos(1.57079632679)....... 4.8965888601467e-012 10 ----------------------------------------------------- math.sin(1.5707963268)........ 1 math.cos(1.5707963268)........ -5.103411967257e-012 9 ----------------------------------------------------- math.sin(1.570796327)......... 1 math.cos(1.570796327)......... -2.0510342851533e-010 8 ----------------------------------------------------- math.sin(1.57079633).......... 1 math.cos(1.57079633).......... -3.2051034546918e-009 7 ----------------------------------------------------- math.sin(1.5707963)........... 1 math.cos(1.5707963)........... 2.6794896585029e-008 6 ----------------------------------------------------- math.sin(1.570796)............ 0.99999999999995 math.cos(1.570796)............ 3.2679489653814e-007 5 ----------------------------------------------------- math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-006 4 ----------------------------------------------------- math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-006 3 ----------------------------------------------------- math.sin(1.571)............... 0.99999997925861 math.cos(1.571)............... -0.00020367320369518 2 ----------------------------------------------------- math.sin(1.57)................ 0.99999968293183 math.cos(1.57)................ 0.00079632671073326

Looks like you should file a bug.  

Note: You can work around this for now with the above rounding code.

Similar results found running Corona Simulator (2015.2605) on OS X 10.10.2:

math.rad( 90 )     1.5707963267949 ... 6 -----------------------------------------------------  math.sin(1.570796)............ 0.99999999999995 math.cos(1.570796)............ 3.2679489653814e-07 5 -----------------------------------------------------  math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-06 4 -----------------------------------------------------  math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-06 3 -----------------------------------------------------  math.sin(1.571)............... 0.99999997925861 math.cos(1.571)............... -0.00020367320369518 2 -----------------------------------------------------  math.sin(1.57)................ 0.99999968293183 math.cos(1.57)................ 0.00079632671073326

Finally, similar results see on iPad Air (Corona 2015.2605) running iOS 8.2:

math.rad( 90 ) 1.5707963267949 ... 6 ----------------------------------------------------- math.sin(1.570796)............ 0.99999999999995 math.cos(1.570796)............ 3.2679489653814e-07 5 ----------------------------------------------------- math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-06 4 ----------------------------------------------------- math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-06 3 ----------------------------------------------------- math.sin(1.571)............... 0.99999997925861 math.cos(1.571)............... -0.00020367320369518 2 ----------------------------------------------------- math.sin(1.57)................ 0.99999968293183 math.cos(1.57)................ 0.00079632671073326

If someone out there ALSO has standalone LUA installed on his/her computer, I would very much like to see if this reproduces in that environment.  (Code may require some changes to run)

Thanks!

@sharp100,

Thanks for putting your code in a code block.   It seems to have been corrupted however.  If you selected a ‘code type’ you don’t need to do that.  You can simply use the default settings.  If not, then … weird.  

In any case thanks for the post and for using the forums formatting tools.

What is a “weird physics response”?

Those values are VERY close to 0 and 1. The “-3.49…e-015” is in -0.000000000000000349…, for instance.

Maybe you’re doing

if x == 0 then

or similar, instead of

if math.abs(x) \< .0001 then -- some tolerance

Very few numbers are representable exactly (integers are, fortunately, up to 2^52).

See also: 

What Every Computer Scientist Should Know About Floating-Point Arithmetic

You know.  After all that running examples, I failed to notice that all the numbers were basically variances on 0.

“Forest for the trees…” I guess.

@sharp100 - StarCrunch is right.  Floating point numbers at that precision will bounce around a lot.  His suggestion to check for a minimum size is a good idea.  

As I suggested previously, you can simply round your inputs, but  you can also round your outputs to get more consistent behavior.

Changing the two output lines from my mod of your code to this:

 print( string.rpad("math.sin(" .. tmp .. ")", 30, "." ), round(math.sin(tmp),3) ) print( string.rpad("math.cos(" .. tmp .. ")", 30, "." ), round(math.cos(tmp),3) ) 

Produces this:

13 ----------------------------------------------------- math.sin(1.5707963267949)..... 1 math.cos(1.5707963267949)..... 0 ... 3 ----------------------------------------------------- math.sin(1.571)............... 1 math.cos(1.571)............... 0 2 ----------------------------------------------------- math.sin(1.57)................ 1 math.cos(1.57)................ 0.001

Thanks to you both!  Rounding is the solution.  It fixes the problems I was experiencing at 90, 180 and 270 and I now have a better understanding of floating point numbers.  I was afraid I was going to have to avoid those 3 numbers for the rest of my Corona career ; )

@StarCrush - thanks, that floating point link you gave us was really helpful.

@roaminggamer - thanks for diving in and helping to verify that, as far as the math.sin and math.cos “glitches” were concerned, I wasn’t nuts. 

Those very slight number discrepancies were throwing off my directional lighting ( compositePaint - object.fill.effect.dirLightDirection ; n-mapped with Sprite Illuminator) which is apparently sensitive enough to be effected by those tiny differences.

It also bothered my “trig engine” which is a module I built to integrate Box2D and display objects when exerting linear and angular impulses - i.e. knowing how hard to push an object relative to its size.  The trig engine has some strange constants like my angular constant that is effectively 0.057 * 0.0021 * 0.000667 a number so small that it was sensitive to those slight floating point variances.

As always, it feels good to have a grasp of why something didn’t work.  Thanks again!

Jonathan

It’s a shot in the dark, but if the effect is using the actual angle (versus just the dot product) under the hood, the dot product itself may be just barely over 1, when dirLightDirection lines up with, say, (0, 0, 1). This is actually a common problem, e.g. see hereunder “angle between unit vectors”.

EDIT : I ought to report this at some point, but that should be “v - u” in both asin () calls.

You didn’t tell us which version of Corona SDK you’re using.  That’s critical for debugging:

The latest daily build as I write this is:  2015.2625

I’ve confirmed similar results for 2015.2610, using this variation on your test.

local function round(val, n) if (n) then return math.floor( (val \* 10^n) + 0.5) / (10^n) else return math.floor(val+0.5) end end function string:rpad(len, char) local theStr = self if char == nil then char = ' ' end return theStr .. string.rep(char, len - #theStr) end --converting 90 degrees to radians local radOut = math.rad( 90 ) print( "math.rad( 90 ) ", radOut) for i = 13, 2, -1 do local tmp = round(radOut, i) print( i, " ----------------------------------------------------- ") print( string.rpad("math.sin(" .. tmp .. ")", 30, "." ), math.sin(tmp) ) print( string.rpad("math.cos(" .. tmp .. ")", 30, "." ), math.cos(tmp) ) end

This produced this output running in the Windows Simulator (2015.2610) on a Windows 7 machine.

math.rad( 90 ) 1.5707963267949 13 ----------------------------------------------------- math.sin(1.5707963267949)..... 1 math.cos(1.5707963267949)..... -3.4914813388431e-015 12 ----------------------------------------------------- math.sin(1.570796326795)...... 1 math.cos(1.570796326795)...... -1.0341155355511e-013 11 ----------------------------------------------------- math.sin(1.57079632679)....... 1 math.cos(1.57079632679)....... 4.8965888601467e-012 10 ----------------------------------------------------- math.sin(1.5707963268)........ 1 math.cos(1.5707963268)........ -5.103411967257e-012 9 ----------------------------------------------------- math.sin(1.570796327)......... 1 math.cos(1.570796327)......... -2.0510342851533e-010 8 ----------------------------------------------------- math.sin(1.57079633).......... 1 math.cos(1.57079633).......... -3.2051034546918e-009 7 ----------------------------------------------------- math.sin(1.5707963)........... 1 math.cos(1.5707963)........... 2.6794896585029e-008 6 ----------------------------------------------------- math.sin(1.570796)............ 0.99999999999995 math.cos(1.570796)............ 3.2679489653814e-007 5 ----------------------------------------------------- math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-006 4 ----------------------------------------------------- math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-006 3 ----------------------------------------------------- math.sin(1.571)............... 0.99999997925861 math.cos(1.571)............... -0.00020367320369518 2 ----------------------------------------------------- math.sin(1.57)................ 0.99999968293183 math.cos(1.57)................ 0.00079632671073326

Looks like you should file a bug.  

Note: You can work around this for now with the above rounding code.

Similar results found running Corona Simulator (2015.2605) on OS X 10.10.2:

math.rad( 90 ) &nbsp;&nbsp;&nbsp;&nbsp;1.5707963267949 ... 6 -----------------------------------------------------&nbsp; math.sin(1.570796)............ 0.99999999999995 math.cos(1.570796)............ 3.2679489653814e-07 5 -----------------------------------------------------&nbsp; math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-06 4 -----------------------------------------------------&nbsp; math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-06 3 -----------------------------------------------------&nbsp; math.sin(1.571)............... 0.99999997925861 math.cos(1.571)............... -0.00020367320369518 2 -----------------------------------------------------&nbsp; math.sin(1.57)................ 0.99999968293183 math.cos(1.57)................ 0.00079632671073326

Finally, similar results see on iPad Air (Corona 2015.2605) running iOS 8.2:

math.rad( 90 ) 1.5707963267949 ... 6 ----------------------------------------------------- math.sin(1.570796)............ 0.99999999999995 math.cos(1.570796)............ 3.2679489653814e-07 5 ----------------------------------------------------- math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-06 4 ----------------------------------------------------- math.sin(1.5708).............. 0.99999999999325 math.cos(1.5708).............. -3.6732051033466e-06 3 ----------------------------------------------------- math.sin(1.571)............... 0.99999997925861 math.cos(1.571)............... -0.00020367320369518 2 ----------------------------------------------------- math.sin(1.57)................ 0.99999968293183 math.cos(1.57)................ 0.00079632671073326

If someone out there ALSO has standalone LUA installed on his/her computer, I would very much like to see if this reproduces in that environment.  (Code may require some changes to run)

Thanks!

@sharp100,

Thanks for putting your code in a code block.   It seems to have been corrupted however.  If you selected a ‘code type’ you don’t need to do that.  You can simply use the default settings.  If not, then … weird.  

In any case thanks for the post and for using the forums formatting tools.

What is a “weird physics response”?

Those values are VERY close to 0 and 1. The “-3.49…e-015” is in -0.000000000000000349…, for instance.

Maybe you’re doing

if x == 0 then

or similar, instead of

if math.abs(x) \< .0001 then -- some tolerance

Very few numbers are representable exactly (integers are, fortunately, up to 2^52).

See also: 

What Every Computer Scientist Should Know About Floating-Point Arithmetic

You know.  After all that running examples, I failed to notice that all the numbers were basically variances on 0.

“Forest for the trees…” I guess.

@sharp100 - StarCrunch is right.  Floating point numbers at that precision will bounce around a lot.  His suggestion to check for a minimum size is a good idea.  

As I suggested previously, you can simply round your inputs, but  you can also round your outputs to get more consistent behavior.

Changing the two output lines from my mod of your code to this:

 print( string.rpad("math.sin(" .. tmp .. ")", 30, "." ), round(math.sin(tmp),3) ) print( string.rpad("math.cos(" .. tmp .. ")", 30, "." ), round(math.cos(tmp),3) ) 

Produces this:

13 ----------------------------------------------------- math.sin(1.5707963267949)..... 1 math.cos(1.5707963267949)..... 0 ... 3 ----------------------------------------------------- math.sin(1.571)............... 1 math.cos(1.571)............... 0 2 ----------------------------------------------------- math.sin(1.57)................ 1 math.cos(1.57)................ 0.001

Thanks to you both!  Rounding is the solution.  It fixes the problems I was experiencing at 90, 180 and 270 and I now have a better understanding of floating point numbers.  I was afraid I was going to have to avoid those 3 numbers for the rest of my Corona career ; )

@StarCrush - thanks, that floating point link you gave us was really helpful.

@roaminggamer - thanks for diving in and helping to verify that, as far as the math.sin and math.cos “glitches” were concerned, I wasn’t nuts. 

Those very slight number discrepancies were throwing off my directional lighting ( compositePaint - object.fill.effect.dirLightDirection ; n-mapped with Sprite Illuminator) which is apparently sensitive enough to be effected by those tiny differences.

It also bothered my “trig engine” which is a module I built to integrate Box2D and display objects when exerting linear and angular impulses - i.e. knowing how hard to push an object relative to its size.  The trig engine has some strange constants like my angular constant that is effectively 0.057 * 0.0021 * 0.000667 a number so small that it was sensitive to those slight floating point variances.

As always, it feels good to have a grasp of why something didn’t work.  Thanks again!

Jonathan

It’s a shot in the dark, but if the effect is using the actual angle (versus just the dot product) under the hood, the dot product itself may be just barely over 1, when dirLightDirection lines up with, say, (0, 0, 1). This is actually a common problem, e.g. see hereunder “angle between unit vectors”.

EDIT : I ought to report this at some point, but that should be “v - u” in both asin () calls.