@@ -1997,6 +1997,8 @@ Generating a Random Numeric Value: the ''random()'' function</h3>
19971997 | <<number [0,1]>>
19981998 </pre>
19991999
2000+ See [[#random-evaluation]] and [[#random-caching]]
2001+ for details on how the function is evaluated.
20002002 Its arguments are:
20012003
20022004 <dl>
@@ -2036,39 +2038,25 @@ Generating a Random Numeric Value: the ''random()'' function</h3>
20362038 chosen uniformly randomly from the possible values
20372039 that result in an in-range value.
20382040
2039- To avoid issues with numeric precision,
2040- if the max value is <em> very close</em> to equalling a stepped value
2041- (above or below it),
2042- the max value is used as the final stepped value instead.
2043- See [[#random-evaluation]] for precise details.
2044-
20452041 <div class=example>
20462042 For example, ''random(100px, 300px, 50px)''
20472043 can only resolve to ''100px'' , ''150px'' , ''200px'' , ''250px'' , or ''300px'' ;
20482044 it will never return a value like ''120px'' .
20492045
2050- While the minimum value is always a possible result,
2051- the maximum value isn't always,
2052- if it's not also a multiple of the step from the minimum.
2053- For example, in ''random(100px, 200px, 30px)'' ,
2054- the largest possible value it can resolve to is ''190px'' ,
2055- 3 steps from the minimum value.
2056-
2057- Numeric precision issues can be avoided by using exact formula
2058- for step values that would be repeating decimals.
2059- For example, in ''random(100px, 200px, 33.3px)'' ,
2060- the largest possible value is ''199.9px'' (not ''200px'' ),
2061- and in ''random(100px, 200px, 33.4px)'' ,
2062- the largest possible value is <strong> ''166.8px'' </strong> ,
2063- because 3 steps from the min would give ''200.1px''
2064- which is outside the allowed range.
2065- But in ''random(100px, 200px, 100px / 3)'' ,
2066- the largest value is guaranteed to be exactly ''200px''
2067- because 3 steps from the min is
2068- (to within the allowed error)
2069- exactly equal to the max.
2046+ Note that the max value might not actually show up as a possible value;
2047+ for example, in ''random(100px, 200px, 30px)'' ,
2048+ the possible values are ''100px'' , ''130px'' , ''160px'' , and ''190px'' .
20702049 </div>
20712050
2051+ Note: Even if it's <em> intended</em> that <code> min</code> and <code> max</code>
2052+ are exactly some integer multiple of <code> step</code> apart,
2053+ numeric precision issues can prevent that from being true.
2054+ To avoid these issues,
2055+ if the max value is <em> very close</em> to equalling a stepped value
2056+ (above or below it),
2057+ the max value is used as the final stepped value instead.
2058+ See [[#random-evaluation]] for precise details.
2059+
20722060 <div class=example>
20732061 As explained in the definition of ''round()'' ,
20742062 CSS has no "natural" precision for values,
@@ -2365,19 +2353,19 @@ Picking a Random Item From a List: the ''random-item()'' function</h3>
23652353 <random-item()> = random-item( <<random-value-sharing>> , [ <<declaration-value>> ? ]# )
23662354 </pre>
23672355
2356+ See [[#random-evaluation]] and [[#random-caching]]
2357+ for details on how the function is evaluated.
2358+
23682359 The <em> required</em> <<random-value-sharing>>
23692360 is interpreted identically to ''random()'' .
23702361 (See [[#random-caching]] for details.)
23712362
23722363 Note: The <<random-value-sharing>> argument is required in ''random-item()'' ,
23732364 but optional in ''random()'' ,
2374- both for parsing reasons
2365+ for parsing reasons
23752366 (it's impossible to tell whether ''random-item(--foo, --bar, --baz)''
23762367 has three <<declaration-value>> arguments
2377- or two and a <<random-value-sharing>> argument),
2378- and because accidentally associating the random generation of ''random-item()'' functions together
2379- is much easier to do accidentally,
2380- since only the number of arguments is used to distinguish instances.
2368+ or two and a <<random-value-sharing>> argument).
23812369
23822370 The remaining arguments are arbitrary sequences of CSS values.
23832371 The ''random-item()'' function is substituted with one of these sequences,
@@ -2412,26 +2400,68 @@ Evaluating Random Values</h3>
24122400
24132401 : for a ''random()'' function with |min|, |max|, and |step|
24142402 ::
2415- * Let |threshold| be <code> |step| / 1000</code> ,
2416- or the smallest representable value in the numeric type being used
2417- if |threshold| would round to zero.
2418- * Let |values| be a list containing all values of the form <code> |min| + |N| * |step|</code> ,
2419- where |N| is a non-negative integer
2420- and the result is less than or equal to |max|.
2421-
2422- If the largest |N| produces a value that is not within |threshold| of |max|,
2423- but |N|+1 would be within |threshold| of |max|,
2424- include the |N|+1 value as well.
2425- * If the largest value in |values| is within |threshold| of |max|,
2426- replace it with |max|.
2427- * Let |length| be the length of |values|.
2428- * Let |random int| be <css> round(down, |R| * |length|, 1)</css> .
2429- * Return the |random int|'th item of |values| (0-indexed).
2403+ * Let |epsilon| be <code> |step| / 1000</code> ,
2404+ or the smallest representable value greater than zero
2405+ in the numeric type being used
2406+ if |epsilon| would round to zero.
2407+ * Let |N| be the largest integer
2408+ such that <code> |min| + |N| * |step|</code> is less than or equal to |max|.
2409+
2410+ If |N| produces a value that is not within |epsilon| of |max|,
2411+ but |N|+1 would produce a value within |epsilon| of |max|,
2412+ set |N| to |N|+1.
2413+ * Let |step index| be a [=random integer=] less than |N|+1, given |R|.
2414+ * Let |value| be <code> |min| + |step index| * |step|</code> .
2415+ * If |step index| is |N| and |value| is within |epsilon| of |max|, return |max|.
2416+ * Otherwise, return |value|.
2417+
2418+ <details class=note>
2419+ <summary> Epsilon/max Details</summary>
2420+
2421+ |epsilon| was chosen to be <code> |step| / 1000</code>
2422+ as a useful "middle ground" value:
2423+ small enough that it's very unlikely to accidentally trigger
2424+ when the author didn't intend it,
2425+ but large enough that it's guaranteed to catch floating-point precision errors.
2426+
2427+ It's size is also within the realm for authors to exploit themselves;
2428+ if their |max| is meant to be an integer multiple of their |step|,
2429+ but the |step| is not a terminating decimal,
2430+ as long as they write the |step| with 5 or 6 digits of precision
2431+ then the largest value should land within |epsilon| of |max|.
2432+
2433+ For example, while ''random(0px, 100px, 33.3px)'' will not generate a ''100px'' value
2434+ (the epsilon is ''.0333px'' ,
2435+ but 3 steps yields ''99.9px'' ,
2436+ which is ''.1px'' from |max|, more than the epsilon),
2437+ ''random(0px, 100px, 33.333px)'' will
2438+ (the epsilon is ''.033333px'' ,
2439+ similar to the previous example,
2440+ but 3 steps yields ''99.999px'' ,
2441+ which is ''.001px'' from |max|, much less than the epsilon).
2442+ (A sufficiently large number of steps
2443+ could still cause enough divergence to defeat this,
2444+ but it works for any remotely reasonable use-case.)
2445+
2446+ Using a [=calculation=] to "exactly" represent non-terminating decimals
2447+ will also work, of course:
2448+ ''random(0px, 100px, 100px / 3)''
2449+ is guaranteed to be able to generate a ''100px'' value,
2450+ as the step value will only be subject to floating-point precision errors
2451+ far smaller than the epsilon.
2452+ </details>
24302453
24312454 : for a ''random-item()'' function with |N| <<declaration-value>> ? arguments:
24322455 ::
2433- * Let |random int| be <css> round(down, |R| * |N|, 1)</css> .
2434- * Return the |random int|'th argument (0-indexed).
2456+ * Let |index| be a [=random integer=] less than |N|, given |R|.
2457+ * Return the |index|'th argument (0-indexed).
2458+
2459+ <div algorithm>
2460+ To <dfn local-lt="random integer">generate a random integer</dfn>
2461+ less than some integer |limit|
2462+ given a [=random base value=] |R|,
2463+ return <css> round(down, |R| * |limit|, 1)</css>
2464+ </div>
24352465
24362466
24372467
0 commit comments