Qwertiy
Qwertiy

Reputation: 21500

How to fix math functions

http://ideone.com/XjMJBa

using System;

public class Test
{
    public static void Main()
    {
        Console.WriteLine(Math.Cos(1e27));
    }
}

Starting from some value Math.Cos and Math.Sin are starting to return their argument instead of value inside of [-1, +1] segment. For example above the output is

1E+27

Why it is so and is it possible to fix such behaviour?

Upvotes: 3

Views: 333

Answers (4)

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186823

Trigonometric functions (Sin, Cos and alike) a periodic with a period of 2 pi (2 * Math.PI). However, since Math.PI is double and thus has 16-17 correct digits only you can't compute 1e27 by naive (we require at least 27 digits):

  Double result = Math.Cos(1e27); // totally wrong.

To perform the task, you need first appropriate pi value, e.g. from here

http://www.piday.org/million/

And we have to work with BigInteger as well:

  int exponent = 27;
  const String piString = "314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054"; 

  BigInteger pi2 = BigInteger.Parse(piString) * 2;
  int scale = piString.Length - 1; // 2 * pi is not that big integer, it's 6.28...
  BigInteger argument = BigInteger.Pow(10, exponent + scale);

  // Now let's compute the double value (fraction) that's reminder of 1e27 % (2 * PI)
  String remainder = (argument % pi2)
    .ToString()
    .PadLeft(piString.Length, '0')
    .Insert(1, "."); // we can have x.xxx, 0.xxx, 0.0xxx etc. remainders 

  Double fraction = 
    Double.Parse("0." + remainder.Substring(0, 20), CultureInfo.InvariantCulture);

  // and now, finally
  Double result = Math.Cos(fraction);

Finally, the actual result is

-0.695977596990354

Note, that naive C++ computation (taken from Qwertiy comment which is 0.849247) is totally wrong, since even C++ double doesn't support pi 27 digits.

Upvotes: 4

Micke
Micke

Reputation: 2309

From the MSDN documentation:

The angle, a, must be in radians. Multiply by Math.PI/180 to convert degrees to radians.

Acceptable values of a range from approximately -9223372036854775295 to approximately 9223372036854775295. For values outside of this range, the Sin method returns a unchanged rather than throwing an exception.

Upvotes: 2

ipavlu
ipavlu

Reputation: 1659

The issue here is the math for Sin, Cos computation and the available number of bits to represent information stored, the real value.

To compute the Cos function, for example as you asked here, you need to compute this:

Cos(x) = 1 - x*x/2! + x*x*x*x/4! - x*x*x*x*x*x/6! + ...

If your number is at left(negative) or right(positive) limit borders of the data type used for storage of number, then you get fast into problems, because if you are near such limit, then computing x^2, x^4, x^6 is breaking outside of the data type limit pretty fast...

Sure, there are ways to chose some less violent algorithms to compute Sin/Cos, but there are limits to that.

The second thing here is, that for a longer time, math computations are thanks to behaviour like this easy and prone to errors. If the method does not mind to compute the result, it should end with an exception to provide information, where it failed. I saw many algorithms, (some were mine :) ), where such details were overlooked, producing magical results, and it ends up with NaN's or like here, returning back same value. It gets pretty hard to hunt these math errors with numbers, limits, etc in complex algorithms...

Upvotes: 0

XlbrlX
XlbrlX

Reputation: 763

"The angle, d, must be in radians. Multiply by Math.PI/180 to convert degrees to radians. Acceptable values of d range from approximately -9223372036854775295 to approximately 9223372036854775295. For values outside this range, the Cos method returns d unchanged rather than throwing an exception."

ref -> https://msdn.microsoft.com/en-us/library/system.math.cos(v=vs.110).aspx

Upvotes: 0

Related Questions