|
2 | 2 | //
|
3 | 3 | // This source file is part of the Swift Numerics open source project
|
4 | 4 | //
|
5 |
| -// Copyright (c) 2021-2024 Apple Inc. and the Swift Numerics project authors |
| 5 | +// Copyright (c) 2021-2025 Apple Inc. and the Swift Numerics project authors |
6 | 6 | // Licensed under Apache License v2.0 with Runtime Library Exception
|
7 | 7 | //
|
8 | 8 | // See https://swift.org/LICENSE.txt for license information
|
9 | 9 | //
|
10 | 10 | //===----------------------------------------------------------------------===//
|
11 | 11 |
|
12 |
| -// TODO: it's unfortunate that we can't specify a custom random source |
13 |
| -// for the stochastic rounding rule, but I don't see a nice way to have |
14 |
| -// that share the API with the other rounding rules, because we'd then |
15 |
| -// have to take either the rule in-out or have an additional RNG/state |
16 |
| -// parameter. The same problem applies to rounding with dithering and |
17 |
| -// any other stateful rounding method. We should consider adding a |
18 |
| -// stateful rounding API down the road to support those use cases. |
19 |
| - |
20 | 12 | /// A rule that defines how to select one of the two representable results
|
21 | 13 | /// closest to a given value.
|
22 | 14 | ///
|
|
59 | 51 | /// 2.0 | 2 | 2 | 2 | 2 | 2 |
|
60 | 52 | /// -------+----------+----------+----------+----------+----------+
|
61 | 53 | ///
|
62 |
| -/// Specialized rounding rules |
| 54 | +/// Specialized rounding rules |
63 | 55 | ///
|
64 |
| -/// value | toOdd | stochastically | requireExact | |
65 |
| -/// =======+==============+=======================+================+ |
66 |
| -/// -1.5 | -1 | 50% -2, 50% -1 | trap | |
67 |
| -/// -------+--------------+-----------------------+----------------+ |
68 |
| -/// -0.5 | -1 | 50% -1, 50% 0 | trap | |
69 |
| -/// -------+--------------+-----------------------+----------------+ |
70 |
| -/// 0.5 | 1 | 50% 0, 50% 1 | trap | |
71 |
| -/// -------+--------------+-----------------------+----------------+ |
72 |
| -/// 0.7 | 1 | 30% 0, 70% 1 | trap | |
73 |
| -/// -------+--------------+-----------------------+----------------+ |
74 |
| -/// 1.2 | 1 | 80% 1, 20% 2 | trap | |
75 |
| -/// -------+--------------+-----------------------+----------------+ |
76 |
| -/// 2.0 | 2 | 2 | 2 | |
77 |
| -/// -------+--------------+-----------------------+----------------+ |
| 56 | +/// value | toOdd | requireExact | |
| 57 | +/// =======+==============+================+ |
| 58 | +/// -1.5 | -1 | trap | |
| 59 | +/// -------+--------------+----------------+ |
| 60 | +/// -0.5 | -1 | trap | |
| 61 | +/// -------+--------------+----------------+ |
| 62 | +/// 0.5 | 1 | trap | |
| 63 | +/// -------+--------------+----------------+ |
| 64 | +/// 0.7 | 1 | trap | |
| 65 | +/// -------+--------------+----------------+ |
| 66 | +/// 1.2 | 1 | trap | |
| 67 | +/// -------+--------------+----------------+ |
| 68 | +/// 2.0 | 2 | 2 | |
| 69 | +/// -------+--------------+----------------+ |
78 | 70 | /// ```
|
79 | 71 | public enum RoundingRule {
|
80 | 72 | /// Produces the closest representable value that is less than or equal
|
@@ -200,45 +192,6 @@ public enum RoundingRule {
|
200 | 192 | /// because 5/2 = 2.5 is equally close to 2 and 3, and 2 is even.
|
201 | 193 | case toNearestOrEven
|
202 | 194 |
|
203 |
| - /// Adds a uniform random value from [0, d) to the value being rounded, |
204 |
| - /// where d is the distance between the two closest representable values, |
205 |
| - /// then rounds the sum downwards. |
206 |
| - /// |
207 |
| - /// Unlike all the other rounding modes, this mode is _not deterministic_; |
208 |
| - /// repeated calls to rounding operations with this mode will generally |
209 |
| - /// produce different results. There is a tradeoff implicit in using this |
210 |
| - /// mode: you can sacrifice _reproducible_ results to get _more accurate_ |
211 |
| - /// results in aggregate. For a contrived but illustrative example, consider |
212 |
| - /// the following: |
213 |
| - /// ``` |
214 |
| - /// let data = Array(repeating: 1, count: 100) |
215 |
| - /// let result = data.reduce(0) { |
216 |
| - /// $0 + $1.divided(by: 3, rounding: rule) |
217 |
| - /// } |
218 |
| - /// ``` |
219 |
| - /// because 1/3 is always the same value between 0 and 1, any |
220 |
| - /// deterministic rounding rule must produce either 0 or 100 for |
221 |
| - /// this computation. But rounding `stochastically` will |
222 |
| - /// produce a value close to 33. The _error_ of the computation |
223 |
| - /// is smaller, but the result will now change between runs of the |
224 |
| - /// program. |
225 |
| - /// |
226 |
| - /// For this simple case a better solution would be to add the |
227 |
| - /// values first, and then divide. This gives a result that is both |
228 |
| - /// reproducible _and_ accurate: |
229 |
| - /// ``` |
230 |
| - /// let result = data.reduce(0, +)/3 |
231 |
| - /// ``` |
232 |
| - /// but this isn't always possible in more sophisticated scenarios, |
233 |
| - /// and in those cases this rounding rule may be useful. |
234 |
| - /// |
235 |
| - /// Examples: |
236 |
| - /// - `(-4).divided(by: 3, rounding: .stochastically)` |
237 |
| - /// will be –1 with probability 2/3 and –2 with probability 1/3. |
238 |
| - /// - `5.shifted(rightBy: 1, rounding: .stochastically)` |
239 |
| - /// will be 2 with probability 1/2 and 3 with probability 1/2. |
240 |
| - case stochastically |
241 |
| - |
242 | 195 | /// If the value being rounded is representable, that value is returned.
|
243 | 196 | /// Otherwise, a precondition failure occurs.
|
244 | 197 | ///
|
@@ -304,15 +257,6 @@ extension FloatingPoint {
|
304 | 257 | // which way that rounds, then select the other value.
|
305 | 258 | let even = (trunc + one/2).rounded(.toNearestOrEven)
|
306 | 259 | return trunc == even ? trunc + one : trunc
|
307 |
| - case .stochastically: |
308 |
| - let trunc = rounded(.towardZero) |
309 |
| - if trunc == self { return trunc } |
310 |
| - // We have eliminated all large values at this point; add dither in |
311 |
| - // ±[0,1) and then truncate. |
312 |
| - let bits = Swift.min(-Self.ulpOfOne.exponent, 32) |
313 |
| - let random = Self(UInt32.random(in: 0 ... (1 << bits &- 1))) |
314 |
| - let dither = Self(sign: sign, exponent: -bits, significand: random) |
315 |
| - return (self + dither).rounded(.towardZero) |
316 | 260 | case .requireExact:
|
317 | 261 | let trunc = rounded(.towardZero)
|
318 | 262 | precondition(isInfinite || trunc == self, "\(self) is not an exact integer.")
|
|
0 commit comments