From https://drafts.csswg.org/css-values/#funcdef-tan,
tan()can return any number between +โ and โโ.
But it's not clear to me what tan(90deg) is supposed to return. Mathematically, the limit from the left is +โ and the limit from the right is โโ, so the limit doesn't exist. Does this mean NaN? But this case is not covered in https://drafts.csswg.org/css-values/#trig-infinities, which only says
In
sin(A),cos(A), ortan(A), if A is infinite, the result is NaN.
For the inverse functions:
In
atan(A), if A is +โ, the result is90deg; if A is -โ, the result is-90deg.
So I guess this should round-trip and tan(90deg) should be +โ and tan(-90deg) be -โ?
ECMAScript doesn't define this case either, but I guess it can be handwaved due to floating-point precision problems, e.g. Math.tan(Math.PI / 2) returns 16331239353195370 because ฯ/2 can't be stored exactly. But in CSS, deg is the canonical unit for <angle>, and 90 is an integer, so I think there shouldn't be any precision problem.
So I guess this should round-trip and tan(90deg) should be +โ and tan(-90deg) be -โ?
I would argue strongly for a solution that supports round-tripping, so that atan(tan(90deg)) returns 90deg. If we can't do that, might as well give up on propagating the infinities through the nested functions at all.
And being approximately consistent with JS implementations is good, too. Even if the JS results are a side effect of numerical precision limits.
I think it's also good to preserve tan(x) = sin(x) / cos(x).
So assuming cos(90deg) = cos(-90deg) = +0 (not -0), then IEEE-754 says
tan(+90deg) = +1 / +0 = +โ
tan(โ90deg) = โ1 / +0 = โโ
I would argue strongly for a solution that supports round-tripping, so that atan(tan(90deg)) returns 90deg
IMO atan(tan(90deg)) = 90deg should just be a consequence, not what we want to enforce.
Note that atan โ tan is not the identity, e.g. atan(tan(2ฯ)) is not 2ฯ (given the usual definition of atan). The identity is tan โ atan, so if we want to preserve this in CSS, what we actually need is tan(atan(+โ)) = +โ. And since atan(+โ) = 90deg, then tan(90deg) = +โ
what we actually need is tan(atan(+โ)) = +โ
Ah, good point. The input to tan() can be an arbitrary angle, but the output from atan will always be restricted to the base circle. But yes, once we reverse the nesting, we still want the consistent behavior.
Interesting question!
I agree that, at minimum, tan(90deg) should equal +inf. Since JS's Math.PI slightly undershoots the precise value, it happens to be true that Math.tan(Math.PI/2 * N) is always a very large (near infinite) positive value when N is a positive odd number, and a very large negative value when N is a negative odd. (But of course, adding a very tiny additional nudge will shift it over the threshold and swap the sign.)
So at least it's consistent with naive usage of JS to say that positive angles give +inf and negative give -inf. ^_^ And then yeah, that gives you roundtripping of either nesting (within the [-90deg, 90deg] range).
Um, I only mentioned 90deg and -90deg because I assumed we would preserve the 360deg periodicity.
Sure, saying tan(90deg) != tan(-90deg) already implies that we loose the 180deg periodicity, but the 360deg one is like very intrinsic to trigonometry. I'm not convinced that we should drop it just to match some floating-point precision problems.
This seems strange to me:
tan(-90deg) = -โ
tan(-90deg + 360deg) = +โ
Interesting. I had not realized that was a direction you might be wanting. That makes some sense, tho.
I had also assumed that keeping a consistent behavior for all equivalent angles (mod 360deg) was implied.
I know there are other places where we use a towards/away from zero rule, but for trigonometry having -90deg match 270deg (and so on) is more important.
@tabatkins There is some problem with your patch, ''tan(atan(1/0))'' and ''tan(atan(-1/0))'' are rendered as โ0))โ. I guess you should escape the slash.
Ah, thanks, fixed. (It was the autolinker misfiring on the division sign, thinking it was a ''for/term'' autolink.)
Most helpful comment
I would argue strongly for a solution that supports round-tripping, so that
atan(tan(90deg))returns 90deg. If we can't do that, might as well give up on propagating the infinities through the nested functions at all.And being approximately consistent with JS implementations is good, too. Even if the JS results are a side effect of numerical precision limits.