11

I'm trying something that I thought should be reasonably simple. I have an angle, a position and a distance and I want to find the X,Y co-ordinates from this information.

With an example input of 90 degrees I convert the value to radians with the following code:

public double DegreeToRadian(float angle)
{
  return Math.PI * angle / 180.0;
}

This gives me 1.5707963267949 radians Then when I use

Math.Cos(radians)

I end up with an an answer of: 6.12303176911189E-17

What the heck is going on? The cosine of 90 degrees should be 0, so why am I getting such a deviance... and more importantly how can I stop it?

elaverick
  • 314
  • 1
  • 2
  • 13
  • 1
    That definitely rounds to 0. Use a format specifier when you convert the value to a string so the user sees it the way they expect. – Cody Gray - on strike May 21 '11 at 15:19
  • Ok, I take the point that the lack of precision is due to the lack of precision in the floating point types, but how does something like Windows Calculator manage to get the answer dead on, does it just cheat and use a lookup table? – elaverick May 21 '11 at 15:45
  • 3
    @elaverick - who says the Windows Calculator is using .Net doubles (it isn't), or any floating point type (it isn't). And who says that it's outputting the precise results of a calculation rather than applying sane rounding rules? – Damien_The_Unbeliever May 21 '11 at 15:48
  • 2
    Calculator was [completely rewritten a few years ago](http://blogs.msdn.com/b/oldnewthing/archive/2004/05/25/141253.aspx) to use arbitrary-precision arithmetic in response to *precisely* such bugs. – Cody Gray - on strike May 21 '11 at 15:49
  • Thanks to everyone that offered answers to this, turns out my visualisation for the data was wrong so I had assumed that it must be due to the level of accuracy offered by the maths. I guess that'll teach me to actually read the output rather than just making assumptions on what it's saying. – elaverick May 21 '11 at 16:19
  • 2
    Your value of pi is off by a few parts in 10 to the 17, so it shouldn't be a surprise that the result is also off by a few parts in 10 to the 17. Pi is rounded off to 16 digits or so. – Eric Lippert May 21 '11 at 18:34
  • @CodyGray-onstrike The link is broken. Could you put an Internet-Archive URL as a replacement? – Stev Aug 23 '23 at 19:26

7 Answers7

15

Let me answer your question with another one: How far do you think 6.12303176911189E-17 is from 0? What you call deviance is actually due to the way floating point numbers are internally stored. I would recommend you reading the following article. In .NET they are stored using the IEEE 754 standard.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 3
    Applying some basic trigonometry, the distance between two particles traveling one light year with that error in angle is 6.1E-17 * 9.4E15 ~= 0.57 = 57cm. I don't think it's worth worrying too much about either. – Wai Ha Lee Dec 11 '15 at 12:29
7

you should use rounding

var radians = Math.PI * degres / 180.0;
var cos = Math.Round(Math.Cos(radians), 2);
var sin = Math.Round(Math.Sin(radians), 2);

the result would be: sin: 1 cos: 0

Xhevo
  • 71
  • 1
  • 3
7

See answers above. Remember that 6.12303176911189E-17 is 0.00000000000000006 (I may have even missed a zero there!) so it is a very, very small deviation.

Edwin Groenendaal
  • 2,304
  • 16
  • 8
3

The other posts are correct about the practical matter of working with floating point implementations which return results with small errors. However, it would be nice if floating point library implementations would preserve the basic identity of well-known functions:

Math.Sin(Math.PI) should equal 0,
Math.Cos(Math.PI) should equal -1,
Math.Sin(Math.PI/2) should equal 1,
Math.Cos(Math.PI/2) should equal 0, etc.

You would expect that a floating point library would respect these and other trigonometric identities, whatever the minor errors in its constant values (e.g. Math.PI).

The fact that you're getting a small error from Math.Cos(Math.PI/2) indicates that the implementation is calculating the result, rather than pulling it from a table. A better implementation of Math.Cos and the other transcendental functions could be more accurate for specific identities.

I'm sure in the case of C#, this behavior is expected, and so Microsoft couldn't change it without affecting existing code. If getting the precise result for specific trigonometric identities matters to you, you might wrap the native floating point functions with some code that checks for well-known inputs.

b1tw153
  • 379
  • 4
  • 3
3

Read up on floating point arithmetic. It is never and can never be exact. Never compare exactly to anything, but check whether the numbers differ by a (small) epsilon.

Pontus Gagge
  • 17,166
  • 1
  • 38
  • 51
  • 5
    It sometimes is exact. Just not all the time. – H H May 21 '11 at 15:21
  • It's precisely as exact as anything else in a computer that isn't using randomness. The problem is the general fixation of most people (including computer programmers) with decimal representations. – Damien_The_Unbeliever May 21 '11 at 15:36
  • 3
    @Damien_The_Unbeliever Completely and utterly wrong. It's inexact because the set of real numbers cannot be accurately represented through *any* physical means; it has nothing to do with the number base. Other things in a computer can be represented exactly ... integers and rational numbers, for instance. Even the problem here can be expressed exactly through symbolic rather than numeric representation, but that can only take you so far. – Jim Balter Jun 05 '14 at 00:49
1

Since the result of the calculation is really close to 0 (zero), you could just use rounding:

Math.Round(result, 4): // 4 decimals, e.g.: 2.1234

So, calculation of sin/cos from radian:

const double Deg = Math.PI / 180;
double sin = Math.Round(Math.Sin(yourRadianValue * Deg), 4);
double cos = Math.Round(Math.Cos(yourRadianValue * Deg), 4); // 0.0000...06 becomes 0

Which if yourRadianValue = 90, returns sin = 1 and cos = 0.

TheLethalCoder
  • 6,668
  • 6
  • 34
  • 69
aniski
  • 1,263
  • 1
  • 16
  • 31
1

As noticed by @b1tw153, it'd be great if exact values were returned for multiples of PI/2. And that's exactly what Microsoft did in their System.Numerics library; if you examine the source code for Matrix3x2.CreateRotation, you'll note they handle n * PI/2 cases manually: https://github.com/Microsoft/referencesource/blob/master/System.Numerics/System/Numerics/Matrix3x2.cs#L325

odalet
  • 1,389
  • 10
  • 18