3

I wrote this test code:

NSLog(@"%g", tan(M_PI / 2.0));

and the output of the console is:

1.63312e+16

The issues is about approximation, right? Did I make some mistakes or the tan function of math.h really doesn't handle this case itself (returning me INFINITY) ? shall I handle theese input cases myself (example: when I get pi/2 input value I return an error message) or is there a better (more elegant) way to get the correct value ?

Thanks

Terry Wilcox
  • 9,010
  • 1
  • 34
  • 36
Massimo
  • 112
  • 2
  • 9

1 Answers1

7

Its because M_PI != real pi because it cannot be represented, so what you get from M_PI is approximation of pi, which its tangent is what you get.
Edit: the following:

printf("cos(M_PI / 2) = %.30f\nsin(M_PI / 2) = %.30f\n",
       cos(M_PI / 2), sin(M_PI / 2));

will print

cos(M_PI / 2) = 0.000000000000000061232339957368
sin(M_PI / 2) = 1.000000000000000000000000000000

Which shows cos(pi / 2) is non-zero.
Doing the division will give

1.63312393531953E16

which is exactly what you get.

Daniel
  • 30,896
  • 18
  • 85
  • 139
  • This is the correct answer, because infinity *can* be represented using floating point numbers. –  Dec 10 '11 at 17:00
  • That's true, but the `sin` and `cos` functions are also approximations, and can handle this correctly. Calling `cos` on `M_PI` will return `-1` correctly, and not an approximation, and `sin` will return `0`. – Itai Ferber Dec 10 '11 at 17:02
  • Thanks but I already knew it in fact that's what I meant with "the issues is about approximation"... My doubt was about tan returning value which is not INFINITY as I expected... – Massimo Dec 10 '11 at 17:06
  • @ItaiFerber: cos doesn't return -1. if you set presicion it will return something like -0.99999999 and sin will return something like 0.00000001. I will edit the question with proof – Daniel Dec 10 '11 at 17:09
  • @Massimo: its returning a different value because you give it a different input. you don't pass in `pi/2`, you pass a value close to it. thats why you don't get output for `pi/2`, you get output for a value close to it. – Daniel Dec 10 '11 at 17:18
  • NSLog(@"%g", cos(M_PI)); gives exactly -1 – Massimo Dec 10 '11 at 17:19
  • I don't know what you get when you run the code, but I get this: http://pastie.org/2996608 (looks pretty exact to me). – Itai Ferber Dec 10 '11 at 17:19
  • @ItaiFerber: its because `%f` is limited precision. to get full precision use `%.30f` – Daniel Dec 10 '11 at 17:20
  • @Massimo: its because `%g` is limited precision. to get full precision use `%.30f` – Daniel Dec 10 '11 at 17:20
  • @Massimo: and the `cos(M_PI)` is not the problem, `cos(M_PI / 2)` is the problem – Daniel Dec 10 '11 at 17:21
  • @Dani I know but since I'm dealing with double precision numbers and sin, cos, etc of math.h deal with double too, I expect, as it is for sin, and cos, that the main input cases (M_PI, M_PI/2, ..) are handled correctly.. – Massimo Dec 10 '11 at 17:23
  • @Massimo: `double` isn't exact. its merely double precision, just as its name suggests. – Daniel Dec 10 '11 at 17:25
  • @Dani thanks but I'm not that ignorant.. :) I know what a double precision number is.. Already took first computer science course ;) Maybe I can't explain quite good because of my poor english.. – Massimo Dec 10 '11 at 17:31
  • @Massimo: I think you are talking about cos and sin having a special case. they might do, but then don't use `M_PI / 2`, use `M_PI_2`, which is the best approximation of `pi / 2` hardcoded (and possible having special cases) – Daniel Dec 10 '11 at 17:33
  • @Dani I wish I could do that, but I can't since is up to the "user" decide which is the number M_PI has to be divided by.. – Massimo Dec 10 '11 at 17:37
  • @Dani `M_PI_2` and `M_PI` / 2 are actually the same number, and unfortunately, don't seem to offer any special cases. (But this is just quibbling.) Regardless, I think we all agree that it's an error that this function doesn't return infinity, right? – Itai Ferber Dec 10 '11 at 17:38
  • @ItaiFerber: its not an error in the function. just because it has a special case on some number that cannot be represented, it shouldn't move that special case to the closest representable number. if it would return infinity for `M_PI / 2`, then I will call it an error in the function, because its the wrong answer. the error originates in `M_PI`, it shouldn't be called that, it should be called `M_CLOSEST_TO_PI` – Daniel Dec 10 '11 at 17:47
  • @Dani Okay, I concede. There's being right, and there's being pragmatic. Yes, you're right - the tangent of any number a little bit off pi / 2 should not return infinity, mathematically at least. But, pragmatically, if you can only represent pi to x digits, then pi to that number of digits should be your baseline for pi and that should be a special case that considers that number to be the true value of pi. And it should consider half that value to be half of pi. Because you cannot get any more precise than that - you're constrained by the system. – Itai Ferber Dec 10 '11 at 18:19
  • @Dani You're saying that the `tan` function should never return infinity or negative infinity because it can never receive the true value of pi / 2 or 3 * pi / 2 (or some other multiple). Mathematically, that is correct. But for the sake of pragmatism, it should accept the closest possible value of pi it could possibly receive and call that pi, because that is the constraints it has to work with. This is where we differ in our lines of thought. Constrained to a certain representation, `M_PI_2` *should be* considered to be pi / 2 because you're not gonna get any closer than that. – Itai Ferber Dec 10 '11 at 18:21
  • 1
    @ItaiFerber: then you get two definitions of tan, programmatically constraint tan, and mathematical tan. whoever wrote the standard library liked mathematical tan better. if you need programmatically constraint tan, like any other function you need thats not in the standard library, you have to write it yourself – Daniel Dec 10 '11 at 18:28
  • I agree with @ItaiFerber.. Since the library defines M_PI as pi it should treat it as pi at all, and work with approximation as the system do with every operations – Massimo Dec 10 '11 at 20:58
  • 1
    @ItaiFerber: I disagree. Any kind of fiddling around with approximate values, as you suggest, would mean that you can't do correct calculations with numbers that really are close to, but not equal to, pi/2. It's not possible to represent pi/2 exactly in floating-point, and anyone writing software that performs floating-point calculations needs to understand that and deal with the consequences. – Keith Thompson Jan 29 '12 at 01:02
  • @KeithThompson You guys are right. My answer is wrong; wish I'd given my argument more thought before taking this topic on in the heat of the moment. Unfortunately, I can't delete my answer because it's the accepted one. For the record, I agree with you. – Itai Ferber Jan 30 '12 at 06:42