tags:

views:

3266

answers:

8

Excuse me for such a newbie question...

If tan(x) = y and atan(y) = x why Math.Atan(Math.Tan(x)) != x??

I´m trying to calculate x in something like

tan(2/x +3) = 5

so

atan(tan(2/x + 3) = atan(5)

and so on... but I´ve tried this:

double d = Math.Atan(Math.Tan(10));

and d != 10... why?

Thanks for your time!

+7  A: 

I dont know any C#, but maths says that tan is not invertable, only in a small intervall.

e.g. tan(pi) = 0 and tan(0) = 0. When asking for atan(0) it could be 0 or pi (or every multiple of pi), so the result is in the range from -pi/2 .. pi/2.

Even if you start with an x in the invertable range i doesnt has to work, because of rounding errors with the floating points (it has not unlimmited precision).

flolo
+32  A: 
  1. The tangent function is periodic with period pi, and is invertible only if you restrict it to a subset of its domain over which it is injective. Usually the choice of such set is the open interval ]-pi/2, pi/2[, hence the arctan function will always return a point in that interval. In your case, 10 = 3*pi + 0.57522... Thus, the arctan of the tangent of 10 will return 0.57522...
  2. Note that the arctan function, defined as above, is injective and defined over all the real numbers, hence the converse of your problem
    math.tan(math.atan(x)) == x 
    indeed holds for each x (except for numerical errors).
  3. In order to deal with numerical errors, you should never perform comparisons between the results of floating point computations using == or !=. Use
    abs(number1 - number2) <  epsilon   // ==
    abs(number1 - number2) >= epsilon   // !=
    
    instead, where epsilon is a small positive constant.
Federico Ramponi
Just note: In C# there is Double.Epsilon constant that represents epsilon value for the double datatype.
lacop
AFAIK (but correct me if I'm wrong) in C# Math.Abs(n1 - n2) < Double.Epsilon is the same as n1 == n2 since by definition anything that has a smaller absolute value than Double.Epsilon is zero. Math.Abs(n1 - n2) <= Double.Epsilon would be better IMHO
DrJokepu
Note that the Double.Epsilon constant represents the smallest positive Double value greater than zero. It does not work in any other domain.
dalle
Peter Ritchie/Thomas Lee: This value is not defined as smallest positive number x, such that x + 1.0 is not equal to 1.0, so Double.Epsilon cannot be used for "almost equality". There exists no constant in the framework whose value is smallest positive number x, such that x + 1.0 is not equal to 1.0
dalle
Yeah, the choice of epsilon is application specific. The framework cannot have some epsilon that works everywhere.
Mehrdad Afshari
A: 

In general, when you are dealing with floating point numbers, you are dealing with approximations. There are numbers that cannot be represented exactly, and the tan and arctan operations are themselves only approximate.

If you want to compare floating point numbers, you need to ask if they are nearly equal, or equivalently, if the difference is less than some small value, and think carefully what you are doing.

Here is are some FAQS (for c++, but the idea is the same), that talk a bit about some of the oddities of floating point numbers:

FAQ 29.16
FAQ 29.17
FAQ 29.18

Edit: Looking at the other answers, I realise that the main problem is probably that tan isn't invertible, but the approximation issue is worth considering too, whenever you test floating point numbers for equality.

Looking at the .net documentation for Math.Atan, atan produces a value between -π/2 and ≤ π/2, which doesn't include 10. That I think is the usual range for arctan.

Silverfish
A: 

It might be helpful if you posted what you are trying to accomplish. I have recollections of discovering trig functions that handled the issue if what quadrant the inputs were in for me when I tried playing with angles, for example.

Brian
+3  A: 

tan-1(tan(x)) == x for all x in (-PI/2, PI/2).

+8  A: 
Tarski
"the problem must soley be down to floating point rounding" is the wrong conclusion, because atan just return from the intervall, does not solve the mathmatical background - just take your pocket calc and (which behaves same) and calc the original example
flolo
A: 

double d = Math.Atan(1) * (180 / Math.PI); so d will be 45 in degrees

sujitha
here Tan-1 return in degree to d
sujitha
A: 
  1. Because the tangent function is periodic we need to normalize input angle. Math.Atan returns an angle, θ, measured in radians, such that -π/2 ≤ θ ≤ π/2, so it makes sense to normalize to that range (since it obviously won't anything within that range anyway):

    double normalizedAngle = (angle + Math.PI / 2) % Math.PI - Math.PI / 2;
    
  2. Doubles should be compared with some error margin. But in fact for this case Double.Epsilon is too small and "If you create a custom algorithm that determines whether two floating-point numbers can be considered equal, you must use a value that is greater than the Epsilon constant to establish the acceptable absolute margin of difference for the two values to be considered equal. (Typically, that margin of difference is many times greater than Epsilon.)" For instance, Math.Atan(Math.Tan(-0.49999632679501449)) + 0.49999632679501449 will be greater than Double.Epsilon for 1.1235582092889474E+307 times.

Regent