Discuss Scratch

fezzinate
Scratcher
100+ posts

Cos(90) doesn't equal 0

Cos(90) should = 0, but in scratch it outputs 6.123233995736766e-17. This is pretty annoying. What's happening?

MeDiaMond
Scratcher
500+ posts
TheLogFather
Scratcher
1000+ posts

Cos(90) doesn't equal 0

That's rounding error for you…

It's probably worth remembering that the internal implementation of cos will likely work in radians rather than degrees, so it will have to take that 90 and multiply by pi/180, and so it will really be calculating cos(pi/2) via various formulae & look-up tables for values in radians.

But pi is an infinite, irrational decimal (transcendental, even!), so it can never internally represent pi/2 exactly (its numbers have to be finite, so must cut off at some point, leaving a very slight inaccuracy for such numbers)…

Just because *you* know cos(90) = 0, why did you expect the computer to be able to *calculate* that value exactly, rather than just getting a results that's extremely close to zero…?


BTW, is it causing any problems for you? Are you trying to test for an exactly zero result for some reason? If so, can you give an example of what you're trying to do with it? -We may be able to suggest an alternative for you!

Last edited by TheLogFather (May 24, 2014 07:53:15)


Siggy the Kumquat slayer:
Main account: DadOfMrLog –– Frameworks for basic pen-rendered 3D in scratch (see studio). Examples:

- - - - 3D Text - - - - - - Simple shapes - - - Controllable structures - - - On the ground - - - - - - In space - - - -

fezzinate
Scratcher
100+ posts

Cos(90) doesn't equal 0

I hadn't considered the possibility of it using radians behind the scenes. Makes a lot of sense.

To answer your question, in the end it doesn't matter all to much for it to be exactly zero in most cases. I had been using it for some time without noticing. What made me notice is I was calculating a 2d vector via angle and the code expected the y-vector to result in 0. Even though I couldn't visually notice a difference in the movement, the code noticed the difference and triggered a sound that was only supposed to be played with you collide with the ground (only possible when the y-vector doesn't equal 0). I spent a long time tracking down the sound bug to discover the cos rounding error.

the fix was simple enough - I simply had to write a few conditionals for when the input angle was 90, 180, and 270 to force them to exact numbers. But it still took me by surprise and seems like something that should be fixed.

Last edited by fezzinate (May 24, 2014 07:59:56)


TheLogFather
Scratcher
1000+ posts

Cos(90) doesn't equal 0

Welcome to real computing!

One of the things you learn very early on when working with floating point numbers on computers is that you should never expect to get exact results - in particular, never compare a variable with a specific, exact value that you write in base 10 (unless you know for sure that somehow it will have that exact value).

It would be impossible to ‘fix’ all the infinity of possible ways that someone might expect to get a specific value from some calculation. Unfortunately, it's really up to the programmer to have some understanding of what they're dealing with - and I don't see how Scratch could be immune from that…

Here's some fun bedtime reading:
http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
http://www.parashift.com/c++-faq/floating-point-arith.html
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Enjoy!

Last edited by TheLogFather (May 24, 2014 08:20:46)


Siggy the Kumquat slayer:
Main account: DadOfMrLog –– Frameworks for basic pen-rendered 3D in scratch (see studio). Examples:

- - - - 3D Text - - - - - - Simple shapes - - - Controllable structures - - - On the ground - - - - - - In space - - - -

fezzinate
Scratcher
100+ posts

Cos(90) doesn't equal 0

totally understand where you're coming from on the exactness of floating point numbers, but in the case of the trigonomic functions, scratch is advertising them as using degrees, not radians. Wouldn't it be possible for scratch to change the code under the hood to optimize for the exactness of degrees rather than radians? I can imagine this potentially being a pretty bad/confusing thing for some scratchers (especially with it being education driven).

here's one example project from a scratcher who made a calculator which features cos. It'd be pretty confusing for them trying to figure out why their program isn't outputting the same results as a real calculator:
http://scratch.mit.edu/projects/1650671/

EDIT: after reading a bit from your links as well as a bit of googling I suppose the best fix that scratch could do is the very same one I did - forcing values for exact whole integer results.

Last edited by fezzinate (May 24, 2014 08:30:42)


TheLogFather
Scratcher
1000+ posts

Cos(90) doesn't equal 0

I wonder whether it's such a good idea to try to ‘shield’ users from the realities of dealing with floating point numbers? The more ‘fixes’ you put in place to try to do this, the harder it becomes to grasp the issue when you finally, inevitably, run into it.

I've seen this kind of thing cause confusion on several occasions:
set [variable v] to [1]
repeat until < (variable) = [0] >
if <some condition...::grey> then
change [variable v] by (-0.1)
end
end
Should Scratch test for strings of the form “a.b”, where a & b are digits, and then have custom routines for calculating with such things…? (A bit like I did for ‘special cases’ of powers of ten in my generalised X^Y custom block project - though note my comments about this in the notes!)


I don't know exactly what your project is doing, so maybe this could never apply… but let's say you edit it at some point so that your directions start to change using some kind of damped angular velocity (a very common method on Scratch), a bit like:
if < key [left arrow v] pressed? >
change [angle vel v] by (-3)
end
if < key [right arrow v] pressed? >
change [angle vel v] by (3)
end
change [angle vel v] by ( (-0.2) * (angle vel) ) // damp the angle velocity towards zero
turn cw (angle vel) degrees

If you do something like that, you'll likely not hit exactly 0, 90, 180 or 270 degrees again, so you'll have to consider some other way to check the kind of thing you mentioned above.


For your case, it sounds like you specifically want to check if the y velocity is zero - presumably so you know when you're ‘standing’ on something, rather than ‘colliding’ with it…?

I know for velocity components I quite often place some kind of lower limit on the absolute size, setting explicitly to zero if below that threshold. i.e. I tend to add a check something like this to above:
...
if < ( [abs v] of (angle vel) ) < (0.1) > then
set [angle vel v] to [0]
else
change [angle vel v] by ( (-0.2) * (angle vel) ) // damp the angle velocity towards zero
turn cw (angle vel) degrees // having this inside 'else' prevents unnecessary work by Scratch when turning by zero!
end

Perhaps doing a similar kind of thing for y velocity, in your case, may help? (Although maybe that y-vel can only come from cos of an angle, in which case, I guess you may want to have some kind of test if the angle is very close to 0, 90, 180, 270, and have it set the x & y vel components explicitly, so you can be sure when testing for exact zero…)

Last edited by TheLogFather (May 24, 2014 10:32:03)


Siggy the Kumquat slayer:
Main account: DadOfMrLog –– Frameworks for basic pen-rendered 3D in scratch (see studio). Examples:

- - - - 3D Text - - - - - - Simple shapes - - - Controllable structures - - - On the ground - - - - - - In space - - - -

fezzinate
Scratcher
100+ posts

Cos(90) doesn't equal 0

To explain my special case in more detail and why it mattered, albeit minorly:

I'm making a side-scrolling platformer. When initializing the game, I placed the player sprite exactly 1px above a collidable floor. The way I have the physics set up, gravity is only applied when you've initiated a jump, otherwise it assumes you are on the ground instead of needlessly pressing you against it causing collision handling every frame. The game starts assuming you're already grounded. To move the player, I apply a force in the form of (angle, velocity) - for right, it's 90 degrees. If y is slightly below zero then it results in a collision detected with the floor. If slightly above, it triggers a jump which then applies gravity which results in a collision detected with the floor. After the collision occurs the y velocity is zeroed out. This code worked fine, and visually it performed as expected. But behind the scenes it was initializing and terminating a jump for every frame you were pressing ‘right’. This only presented itself as a problem once I attempted to add sounds for collision and was bombarded with hundreds simultaneous sounds.

I know there are many different ways this could have been easily solved; For example, I could have manipulated the x,y velocities directly rather than through an angle. Or I could have triggered my collision sound in a different way. Or I could threshold the velocities. The fix was never really a problem. I chose to force the values for axis-angles just because it fit well with the pattern I was coding with. But it still seemed rather odd to me that this “bug” existed in the first place.

You've made excellent points about why it shouldn't be considered a bug, but my personal preference remains treating degrees as they are advertised. the Cos(90 degrees) is 0. that's just a fact. A calculator will tell you 0. A teacher will tell you 0. Trying to explain why it's off to a young scratcher doesn't seem appropriate in this instance. Unlike other floating point rounding errors, these seem to warrant exception because we're talking about whole integer expectations. It's not like we're expecting the precision of pi or the sqrt(2). Unlike pi, cos(90) is not an irrational number, but it was being treated as such.

But then again… you know… what's the likelihood that a younger scratcher is paying this close attention to trigonometric functions anyway? *shrug*

Last edited by fezzinate (May 24, 2014 11:20:50)


desargues
Scratcher
2 posts

Cos(90) doesn't equal 0

What about this:

if <(angle) mod (180) = [90 ]> then

set [result] to [NaN]
set [ErrMsg] to [tan of angle=(angle) is not defined]
broadcast [Error]
else
set [result] to [([tan] of (x)) ]
end

It makes sense to use such approach in arithmetic calculations to inform users about errors.
I used similar blocks for square roots or logs of negative numbers.

Powered by DjangoBB

Standard | Mobile