locked
Math.Sin, Math.Cos issue with trigonometric thumb rule. RRS feed

  • Question

  • The thumb rule of trigonometry is sin(degree) * sin(degree) + cos(degree) * cos(degree) = 1,

    for the following example i am not getting those results.

    I am using the value 0.71001282198650217

    double deg = 0.71001282198650217;
    double result = System.Math.Sin(deg) * System.Math.Sin(deg) + System.Math.Cos(deg) * System.Math.Cos(deg);
    
    // here result should be 1.0, but i am getting 0.99999999999999989
    Console.WriteLine(result.Tostring()); 
    

    which is wrong completely.

    Can anybody explain me how i can get 1.0 as the result here?


    • Edited by Naveen L Friday, May 4, 2012 1:02 PM marked code.
    Friday, May 4, 2012 12:58 PM

Answers

  • Some comments which you can cheerfully ignore.

    First, as far as I know Math calculations use IEEE arithmetic so are correct to 15 decimal places. You won't get any more accurate than that. For a particular formula you might get much less, depending on how you arrange the calculations. That's a topic in numerical analysis.

    Secondly, if you have a formula that contains "sin(a)*sin(a) + cos(a)*cos(a)" then simply replace it with 1, since that is true for all values of a. I suspect that the formula you refer to has something like "sin(a)*sin(b) + cos(a)*cos(b)". In that case of course you can't replace with 1, but there might be a rearrangement (perhaps using tan or some other trig function) that is numerically better behaved. But you still won't get better than 15 d.p.

    Thirdly, if the built in sin and cos are not accurate enough you could of course write your own functions e.g. as a series sum (which I suspect is what Math.Sin does).


    Regards David R
    ---------------------------------------------------------------
    The great thing about Object Oriented code is that it can make small, simple problems look like large, complex ones.
    Object-oriented programming offers a sustainable way to write spaghetti code. - Paul Graham.
    Every program eventually becomes rococo, and then rubble. - Alan Perlis
    The only valid measurement of code quality: WTFs/minute.

    • Proposed as answer by servy42 Friday, May 4, 2012 2:40 PM
    • Marked as answer by Mike Dos Zhang Thursday, May 10, 2012 10:47 AM
    Friday, May 4, 2012 2:31 PM
  • @Scotty & Ahmed

    The problem can't just be fixed by rounding the result.  Clearly the entire design of the program is based around the fact that doubles have infinite precision, and they don't.  The program itself will likely need to be more fundamentally altered.  Also keep in mind that if you have enough computations you can actually be off by more than one unit of whatever digit you round to, so just rounding can make things much worse.

    @Naveen

    Doubles don't have infinite precision.  You will need to alter the design of your program to account for this fact.  One common strategy is not to compare doubles using straight equality, but rather to say that two doubles are "equal" if the difference between them is less then some epsilon, i.e.

    public const double epsilon = 0.000001;
    
    public bool DoubleEquals(double first, double second)
    {
      return Math.Abs(first-second) < epsilon;
    }

    If you use that definition of "equality" then 0.99999999999999989 is in fact equal to 1 and your program should work as expected.

    Rice's answer will also help with strategies to reduce (but not eliminate) the floating point errors in your code; they should be used in addition to this answer.


    • Edited by servy42 Friday, May 4, 2012 2:49 PM
    • Marked as answer by Mike Dos Zhang Thursday, May 10, 2012 10:47 AM
    Friday, May 4, 2012 2:48 PM

All replies

  • This is expected, due to numerical approximation errors, you need to do rounding.

    you could use the math.ceiling or math.round to correct the result.

    to proof my theroy open windows calculator and calculate the cube root of 5

    1.7099759466766969893531088725439

    now close it

    and  type 1.7099759466766969893531088725439 then calculate the cube for it

    it will be 

    5.0000000000000000000000000000003


    Regards,
    Ahmed Ibrahim
    SQL Server Setup Team
    This posting is provided "AS IS" with no warranties, and confers no rights. Please remember to click "Mark as Answer" and "Vote as Helpful" on posts that help you.
    This can be beneficial to other community members reading the thread.

    Friday, May 4, 2012 1:05 PM
  • result.ToString("0.0")
    Friday, May 4, 2012 1:08 PM
  • hi Ahmed,

    I do agree with you, how ever i can not take that chance, because i am trying to calculate distance between two lat/lons using the standard formula.

    but, that formula is internally depend on sin(degree) * sin(degree) + cos(degree) * cos(degree) = 1 rule.

    any how thanks for the attempt to answer my question.

    Friday, May 4, 2012 1:54 PM
  • Some comments which you can cheerfully ignore.

    First, as far as I know Math calculations use IEEE arithmetic so are correct to 15 decimal places. You won't get any more accurate than that. For a particular formula you might get much less, depending on how you arrange the calculations. That's a topic in numerical analysis.

    Secondly, if you have a formula that contains "sin(a)*sin(a) + cos(a)*cos(a)" then simply replace it with 1, since that is true for all values of a. I suspect that the formula you refer to has something like "sin(a)*sin(b) + cos(a)*cos(b)". In that case of course you can't replace with 1, but there might be a rearrangement (perhaps using tan or some other trig function) that is numerically better behaved. But you still won't get better than 15 d.p.

    Thirdly, if the built in sin and cos are not accurate enough you could of course write your own functions e.g. as a series sum (which I suspect is what Math.Sin does).


    Regards David R
    ---------------------------------------------------------------
    The great thing about Object Oriented code is that it can make small, simple problems look like large, complex ones.
    Object-oriented programming offers a sustainable way to write spaghetti code. - Paul Graham.
    Every program eventually becomes rococo, and then rubble. - Alan Perlis
    The only valid measurement of code quality: WTFs/minute.

    • Proposed as answer by servy42 Friday, May 4, 2012 2:40 PM
    • Marked as answer by Mike Dos Zhang Thursday, May 10, 2012 10:47 AM
    Friday, May 4, 2012 2:31 PM
  • @Scotty & Ahmed

    The problem can't just be fixed by rounding the result.  Clearly the entire design of the program is based around the fact that doubles have infinite precision, and they don't.  The program itself will likely need to be more fundamentally altered.  Also keep in mind that if you have enough computations you can actually be off by more than one unit of whatever digit you round to, so just rounding can make things much worse.

    @Naveen

    Doubles don't have infinite precision.  You will need to alter the design of your program to account for this fact.  One common strategy is not to compare doubles using straight equality, but rather to say that two doubles are "equal" if the difference between them is less then some epsilon, i.e.

    public const double epsilon = 0.000001;
    
    public bool DoubleEquals(double first, double second)
    {
      return Math.Abs(first-second) < epsilon;
    }

    If you use that definition of "equality" then 0.99999999999999989 is in fact equal to 1 and your program should work as expected.

    Rice's answer will also help with strategies to reduce (but not eliminate) the floating point errors in your code; they should be used in addition to this answer.


    • Edited by servy42 Friday, May 4, 2012 2:49 PM
    • Marked as answer by Mike Dos Zhang Thursday, May 10, 2012 10:47 AM
    Friday, May 4, 2012 2:48 PM