4

I am trying to find the correct domain for the tan function, I know that

tan x = sinx / cos x

and tan is undefined when cos x = 0. So

I am trying to check if cos x is 0.

 if ( Math.Cos(x).Equals(0) )
 {
     // do stuff
 }

But this is never true because Math.Cos returns 6.123....E-17 How o check for cos == 0 ?

xanatos
  • 109,618
  • 12
  • 197
  • 280
giannisf
  • 2,479
  • 1
  • 17
  • 29

3 Answers3

11

You need to broaden your expectations a bit - in order for Math.Cos(x) to be genuinely equal to 0, you'd either need inaccuracy in Cos (which will happen, of course) or for x to have an irrational value.

Instead, you should work out some tolerance - some range of values for Math.Cos which is very close to 0. For example:

if (Math.Abs(Math.Cos(x)) < 1e-15)

That 1e-15 is picked pretty much arbitrarily - you should work out what you want it to be for your particular task. (This will still give pretty enormous tan values, of course...)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • What about using `double.Epsilon`? – user1620220 Feb 24 '15 at 15:52
  • 2
    `double.Epsilon` is far too small. – Matthew Watson Feb 24 '15 at 15:53
  • http://stackoverflow.com/questions/328475/should-we-compare-floating-point-numbers-for-equality-against-a-relative-error – Alexei Levenkov Feb 24 '15 at 15:53
  • @user1620220: No, that's likely to be too small. In particular, *only* the values 0, `double.Epsilon` and `-double.Epsilon` are within `double.Epsilon` of 0... and I wouldn't like to say whether there is a `double` value `x` such that `Cos(x)` is one of those three values. – Jon Skeet Feb 24 '15 at 15:53
  • 3
    double.Epsilon is not an epsilon for fuzzy comparisson, but rather the smallest value that is not zero and can be represented in double-precision floating point number. – JustSomeGuy Feb 24 '15 at 15:53
  • 1
    It's worth noting that usually these threshold values (ie :`1e-15`) will often depend on the nature of further calculations down the line. In some cases (multiple derivatives, etc) you actually have to truncate much sooner than in others just to avoid the numbers exploding in subsequent calculations. Your overall system will have a "generally calculable" domain and you typically have to figure out what that is in each situation. – J... Feb 24 '15 at 15:54
  • For cos(x) approaching 0, tan(x) approaches 1/cos(x). So the tolerance can be selected such that tan(x) is maximum, hence it will be something around 1/double.MaxValue = 5.5626846E-309 >>> double.Epsilon = 4.940656E-324. Right? – Bhaskar Feb 24 '15 at 16:10
  • @L16H7: Well, that assumes that the rest of the code can handle huge values too. In reality, the OP may well want to restrict the range to smaller values. It really depends on the rest of the OP's task - it's very context sensitive. – Jon Skeet Feb 24 '15 at 16:13
  • I know practically it depends on the task. I was trying to calculate the theoretical minimum value cos(x) can get for tan(x) to be below the MaxValue. – Bhaskar Feb 24 '15 at 16:45
4

If your problem is receiving an exception from Math.Tan, Math.Tan doesn't throw. The only thing it can do is return Double.NaN and (perhaps) Double.PositiveInfinity/Double.NegativeInfinity, so simply check for them:

double t = Math.Tan(something);

if (!double.IsInfinity(t) && !double.IsNaN(t))
{
    // Do Something
}
xanatos
  • 109,618
  • 12
  • 197
  • 280
2

The problem is that you need to compare it not directly, but with some accuracy due to the fact how floating point values are represented in memory.

You can fix it by using some code like this one:

const double Epsilon = 0.0001;

if (Math.Cos(x) < Epsilon)
{
    // Code here
}

Also, there is not point in writing Math.Cos(x).Equals(something) because it makes code harder to read.

If you want more info on why your code doesn't work you can look it up here: http://www.parashift.com/c++-faq/floating-point-arith.html

This is a FAQ for C++, but same things apply in your case.

JustSomeGuy
  • 3,677
  • 1
  • 23
  • 31