Skip to content

Commit b8935e7

Browse files
committed
[css-values-4] Add trig and exponent functions, as resolved at the F2F. Fixes w3c#2331.
1 parent 1f8f9e8 commit b8935e7

File tree

1 file changed

+241
-0
lines changed

1 file changed

+241
-0
lines changed

css-values-4/Overview.bs

+241
Original file line numberDiff line numberDiff line change
@@ -2465,6 +2465,247 @@ Comparison Functions: ''min()'', ''max()'', and ''clamp()''</h3>
24652465
</div>
24662466

24672467

2468+
<h3 id=trig-funcs>
2469+
Trigonometric Functions: ''sin()'', ''cos()'', ''tan()'', ''asin()'', ''acos()'', ''atan()'', and ''atan2()''</h3>
2470+
2471+
The trigonometric functions--
2472+
''sin()'', ''cos()'', ''tan()'', ''asin()'', ''acos()'', ''atan()'', and ''atan2()''--
2473+
compute the various basic trigonometric relationships.
2474+
2475+
The <dfn lt="sin()">sin(A)</dfn>, <dfn lt="cos(A)">cos()</dfn>, and <dfn lt="tan()">tan(A)</dfn> functions
2476+
all contain a single [=calculation=]
2477+
which must resolve to either a <<number>>
2478+
or an <<angle>>,
2479+
and compute their corresponding function
2480+
by interpreting the result of their argument as radians.
2481+
(That is, ''sin(45deg)'', ''sin(.125)'', and ''sin(3.14159 / 4 * 1rad)''
2482+
all represent the same value,
2483+
approximately ''.707''.)
2484+
They all represent a <<number>>;
2485+
''sin()'' and ''cos()'' will always return a number between 0 and 1,
2486+
while ''tan()'' can return any number between +∞ and −∞.
2487+
(See [[#calc-type-checking]] for details on how [=math functions=] handle ∞.)
2488+
2489+
The <dfn lt="asin()">asin(A)</dfn>, <dfn lt="acos()">acos(A)</dfn>, and <dfn lt="atan()">atan(A)</dfn> functions
2490+
are the "arc" or "inverse" trigonometric functions,
2491+
representing the inverse function to their corresponding "normal" trig functions.
2492+
All of them contain a single [=calculation=]
2493+
which must resolve to a <<number>>,
2494+
and compute their corresponding function,
2495+
interpreting their result as a number of radians,
2496+
representing an <<angle>>.
2497+
The angle returned by ''asin()'' must be normalized to the range [''-90deg'', ''90deg''];
2498+
the angle returned by ''acos()'' to the range [''0deg'', ''180deg''];
2499+
and the angle returned by ''atan()'' to the range [''-90deg'', ''90deg''].
2500+
2501+
The <dfn lt="atan2()">atan2(A, B)</dfn> function
2502+
contains two comma-separated [=calculations=] A and B
2503+
that must resolve to <<number>>s,
2504+
and returns the <<angle>>
2505+
between the positive X-axis and the point (B,A).
2506+
The returned angle must be normalized to the interval (''-180deg'', ''180deg'']
2507+
(that is, greater than ''-180deg'', and less than or equal to ''180deg'').
2508+
2509+
Note: ''atan2(Y, X)'' is <em>generally</em> equivalent to ''atan(Y / X)'',
2510+
but it gives a better answer when the point in question is in the second (NW) or third (SW) quadrants of the plane.
2511+
''atan2(1, -1)'', corresponding to the point (-1, 1) in the NW of the plane,
2512+
returns ''135deg'',
2513+
distinct from ''atan2(-1, 1)'', corresponding to the point (1, -1) in the SE of the plane,
2514+
which returns ''-45deg''.
2515+
However, as ''1 / -1'' and ''-1 / 1'' both resolve to ''-1'',
2516+
''atan()'' returns the same ''-45deg'' for both points.
2517+
2518+
2519+
<h4 id="trig-infinities">
2520+
Argument Ranges</h4>
2521+
2522+
In ''sin(A)'', ''cos(A)'', or ''tan(A)'',
2523+
if A is infinite,
2524+
the result is NaN.
2525+
(See [[#calc-type-checking]] for details on how [=math functions=] handle NaN.)
2526+
2527+
In ''asin(A)'' or ''acos(A)'',
2528+
if A is less than -1 or greater than 1,
2529+
the result is NaN.
2530+
2531+
In ''atan(A)'',
2532+
if A is +∞,
2533+
the result is ''90deg'';
2534+
if A is -∞,
2535+
the result is ''-90deg''.
2536+
2537+
In ''atan2(A, B)'',
2538+
if A and B are both zero,
2539+
the result is ''0deg''.
2540+
If either or both [=calculations=] are infinite,
2541+
the function must return an angle
2542+
as if the infinite value was replaced by ''1'' (for +∞) or ''-1'' (for -∞)
2543+
and the finite value was replaced by ''0'':
2544+
''atan2(finite, ∞)'' must return ''0deg'', as if it were ''atan2(0, 1)'';
2545+
''atan2(∞, ∞)'' must return ''45deg'', as if it were ''atan2(1, 1)'';
2546+
and so on around the circle.
2547+
2548+
Note: All of these behaviors are intended to match the "standard" definitions of these functions
2549+
as implemented by most programming languages,
2550+
in particular as implemented in JS.
2551+
2552+
2553+
<h3 id=exponent-funcs>
2554+
Exponential Functions: ''pow()'', ''sqrt()'', ''hypot()''</h3>
2555+
2556+
The exponential functions--
2557+
''pow()'', ''sqrt()'', and ''hypot()''--
2558+
compute various exponential functions with their arguments.
2559+
2560+
The <dfn lt="pow()">pow(A, B)</dfn> function
2561+
contains two comma-separated [=calculations=] A and B,
2562+
both of which must resolve to <<number>>s,
2563+
and returns the result of raising A to the power of B,
2564+
returning the value as a <<number>>.
2565+
2566+
The <dfn lt="sqrt()">sqrt(A)</dfn> function
2567+
contains a single [=calculation=]
2568+
which must resolve to a <<number>>,
2569+
and returns the square root of the value
2570+
as a <<number>>.
2571+
(''sqrt(X)'' and ''pow(X, .5)'' are equivalent;
2572+
''sqrt()'' is a common enough function
2573+
that it is provided as a convenience.)
2574+
2575+
The <dfn lt="hypot()">hypot(A,B)</dfn> function
2576+
contains two comma-separated [=calculations=] A and B,
2577+
and returns the length of the hypotenuse of a right-angled triangle
2578+
with legs equal to A and B.
2579+
A and B can resolve to any <<number>>, <<dimension>>, or <<percentage>>,
2580+
but must have the <em>same</em> [=determine the type of a calculation|type=],
2581+
or else the function is invalid;
2582+
the result will have the same [=CSSNumericValue/type=] as the arguments.
2583+
2584+
<details class=note>
2585+
<summary>Why does ''hypot()'' allow dimensions (values with units), but ''pow()'' and ''sqrt()'' only work on numbers?</summary>
2586+
2587+
You are allowed to write expressions like ''hypot(30px, 40px)'',
2588+
which resolves to ''50px'',
2589+
but you aren't allowed to write the expression
2590+
''sqrt(pow(30px, 2) + pow(40px, 2))'',
2591+
despite the two being equivalent in most mathematical systems.
2592+
2593+
There are two reasons for this:
2594+
numeric precision in the exponents,
2595+
and clashing expectations from authors.
2596+
2597+
First, numerical precision.
2598+
For a [=CSSNumericValue/type=] to [=CSSNumericValue/match=] a CSS production like <<length>>,
2599+
it needs to have a single unit with its exponent set to exactly 1.
2600+
Theoretically, expressions like ''pow(pow(30px, 3), 1/3)'' should result in exactly that:
2601+
the inner ''pow(30px, 3)'' would resolve to a value of 27000 with a [=CSSNumericValue/type=] of «[ "length" → 3 ]»
2602+
(aka <<length>>³),
2603+
and then the ''pow(X, 1/3)'' would cube-root the value back down to 30 and multiply the exponent by 1/3,
2604+
giving «[ "length" → 1 ]»,
2605+
which [=CSSNumericValue/matches=] <<length>>.
2606+
In the realm of pure mathematics, that's guaranteed to work out;
2607+
in the real-world of computers using binary floating-point arithmetic,
2608+
in some cases the powers might not exactly cancel out,
2609+
leaving you with an invalid [=math function=]
2610+
for confusing, hard-to-track-down reasons.
2611+
(For a JS example,
2612+
evaluate <code>Math.pow(Math.pow(30, 10/3), .1+.1+.1)</code>;
2613+
the result is not exactly 30,
2614+
because <code>.1+.1+.1</code> is not exactly 3/10.
2615+
Instead, <code>(10/3) * (.1 + .1 + .1)</code> is <em>slightly greater</em> than 1.)
2616+
2617+
Requiring authors to cast their value down into a number,
2618+
do all the math on the raw number,
2619+
then finally send it back to the desired unit,
2620+
while inconvenient,
2621+
ensures that numerical precision won't bite anyone:
2622+
''calc(pow(pow(30px / 1px, 3), 1/3) * 1px)'' is guaranteed to resolve to a <<length>>,
2623+
with a value that, if not exactly 30, is at least very close to 30,
2624+
even if numerical precision actually prevents the powers from exactly canceling.
2625+
2626+
Second, clashing expectations.
2627+
It's not uncommon for authors to expect ''pow(30px, 2)''
2628+
to result in ''900px''
2629+
(such as in <a href="https://github.com/sass/sass/issues/684">this Sass issue</a>);
2630+
that is,
2631+
just squaring the numerical value
2632+
and leaving the unit alone.
2633+
This, however, means the result is dependent on what unit you're expressing the argument in;
2634+
if ''1em'' is ''16px'',
2635+
then ''pow(1em, 2)'' would give ''1em'',
2636+
while ''pow(16px, 2)'' would give ''256px'', or ''16em'',
2637+
which are very different values for what should otherwise be identical input arguments!
2638+
This sort of input dependency is troublesome for CSS,
2639+
which generally allows values to be [=canonical unit|canonicalized=] freely;
2640+
it also makes more complex expressions like ''pow(2em + 10px, 2)'' difficult to interpret.
2641+
2642+
Again, requiring authors to cast their value down into a number
2643+
and then back up again into the desired unit
2644+
sidesteps these issues;
2645+
''pow(30, 2)'' is indeed ''900'',
2646+
and the author can interpret that however they wish.
2647+
2648+
<hr>
2649+
2650+
On the other hand, ''hypot()'' doesn't suffer from these problems.
2651+
Numerical precision in units isn't a concern,
2652+
as the inputs and output all have the same type.
2653+
The result isn't unit-dependent, either,
2654+
due to the nature of the operation;
2655+
''hypot(3em, 4em)'' and ''hypot(48px, 64px)'' give the same result in both cases:
2656+
''5em'' or ''80px''.
2657+
Thus it's fine to let author use dimensions directly in ''hypot()''.
2658+
</details>
2659+
2660+
<h4 id="exponent-infinities">
2661+
Argument Ranges</h4>
2662+
2663+
In ''pow(A, B)'':
2664+
2665+
<dl class=switch>
2666+
: if A is ±∞ and B is 0
2667+
:: the result is 1
2668+
: if A is +∞ and B is greater than 0
2669+
:: the result is +∞
2670+
: if A is +∞ and B is less than 0
2671+
:: the result is 0
2672+
: if A is -∞ and B is greater than 0
2673+
:: the result is -∞ if B is an odd integer, +∞ otherwise
2674+
: if A is -∞ and B is less than 0
2675+
:: the result is 0⁻ if B is an odd integer, 0⁺ otherwise
2676+
2677+
: if A is in the (exclusive) range (-1, 1) and B is +∞
2678+
:: the result is 0⁺
2679+
: if A is 1 or -1 and B is +∞
2680+
:: the result is NaN
2681+
: if A is less than -1 or greater than 1, and B is +∞
2682+
:: the result is +∞
2683+
2684+
: if A is in the (exclusive) range (-1, 1) and B is -∞
2685+
:: the result is +∞
2686+
: if A is 1 or -1 and B is -∞
2687+
:: the result is NaN
2688+
: if A is less than -1 or greater than 1, and B is -∞
2689+
:: the result is 0⁺
2690+
</dl>
2691+
2692+
In ''sqrt(A)'',
2693+
if A is +∞,
2694+
the result is +∞.
2695+
If A is less than 0,
2696+
the result is NaN.
2697+
2698+
In ''hypot(A, B)'',
2699+
if either or both values are infinite,
2700+
the result is +∞.
2701+
2702+
(See [[#calc-type-checking]] for details on how [=math functions=] handle NaN and infinities.)
2703+
2704+
Note: All of these behaviors are intended to match the "standard" definitions of these functions
2705+
as implemented by most programming languages,
2706+
in particular as implemented in JS.
2707+
2708+
24682709
<h3 id='calc-syntax'>
24692710
Syntax</h3>
24702711

0 commit comments

Comments
 (0)