Skip to content

Commit ac7a9a9

Browse files
Merge pull request #185 from stephentyrone/reciprocal-documentation
Further expand documentation of `reciprocal` with some example use.
2 parents d7e692e + 12148ff commit ac7a9a9

File tree

3 files changed

+91
-3
lines changed

3 files changed

+91
-3
lines changed

Sources/ComplexModule/Arithmetic.swift

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,31 @@ extension Complex: AdditiveArithmetic {
5252
// and turn these into operators if/when we have it.
5353
// (https://github.com/apple/swift-numerics/issues/12)
5454
extension Complex {
55+
/// `self` scaled by `a`.
5556
@usableFromInline @_transparent
5657
internal func multiplied(by a: RealType) -> Complex {
58+
// This can be viewed in two different ways, which are mathematically
59+
// equivalent: either we are computing `self * Complex(a)` (i.e.
60+
// converting `a` to be a complex value, and then using the complex
61+
// multiplication) or we are using the scalar product of the vector
62+
// space structure: `Complex(a*real, a*imaginary)`.
63+
//
64+
// Although these two interpretations are _mathematically_ equivalent,
65+
// they will generate different representations of the point at
66+
// infinity in general. For example, suppose `self` is represented by
67+
// `(infinity, 0)`. Then `self * Complex(1)` would evaluate as
68+
// `(1*infinity - 0*0, 0*infinity + 1*0) = (infinity, nan)`, but
69+
// the vector space interpretation produces `(infinity, 0)`. This does
70+
// not matter much, because these are two representations of the same
71+
// semantic value, but note that one requires four multiplies and two
72+
// additions, while the one we use requires only two real multiplications.
5773
Complex(x*a, y*a)
5874
}
5975

76+
/// `self` unscaled by `a`.
6077
@usableFromInline @_transparent
6178
internal func divided(by a: RealType) -> Complex {
79+
// See implementation notes for `multiplied` above.
6280
Complex(x/a, y/a)
6381
}
6482
}
@@ -147,17 +165,23 @@ extension Complex: AlgebraicField {
147165
/// isolated division, but if you are dividing many values by a single
148166
/// denominator, this will often be a significant performance win.
149167
///
150-
/// Typical use looks like this:
168+
/// A typical use case looks something like this:
151169
/// ```
152170
/// func divide<T: Real>(data: [Complex<T>], by divisor: Complex<T>) -> [Complex<T>] {
153-
/// // If divisor is well-scaled, use multiply by reciprocal.
171+
/// // If divisor is well-scaled, multiply by reciprocal.
154172
/// if let recip = divisor.reciprocal {
155173
/// return data.map { $0 * recip }
156174
/// }
157175
/// // Fallback on using division.
158176
/// return data.map { $0 / divisor }
159177
/// }
160178
/// ```
179+
///
180+
/// Error Bounds:
181+
/// -
182+
/// Unlike real types, when working with complex types, multiplying by the
183+
/// reciprocal instead of dividing cannot change the result. If the
184+
/// reciprocal is non-nil, the two computations are always equivalent.
161185
@inlinable
162186
public var reciprocal: Complex? {
163187
let recip = 1/self

Sources/RealModule/AlgebraicField.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,30 @@ public protocol AlgebraicField: SignedNumeric {
6969
/// Note that `.zero.reciprocal`, somewhat surprisingly, is *not* nil
7070
/// for `Real` or `Complex` types, because these types have an
7171
/// `.infinity` value that acts as the reciprocal of `.zero`.
72+
///
73+
/// If `b.reciprocal` is non-nil, you may be able to replace division by `b`
74+
/// with multiplication by this value. It is not advantageous to do this
75+
/// for an isolated division unless it is a compile-time constant visible
76+
/// to the compiler, but if you are dividing many values by a single
77+
/// denominator, this will often be a significant performance win.
78+
///
79+
/// Note that this will slightly perturb results for fields with approximate
80+
/// arithmetic, such as real or complex types--using a normal division
81+
/// is generally more accurate--but no catastrophic loss of accuracy will
82+
/// result. For fields with exact arithmetic, the results are necessarily
83+
/// identical.
84+
///
85+
/// A typical use case looks something like this:
86+
/// ```
87+
/// func divide<T: AlgebraicField>(data: [T], by divisor: T) -> [T] {
88+
/// // If divisor is well-scaled, multiply by reciprocal.
89+
/// if let recip = divisor.reciprocal {
90+
/// return data.map { $0 * recip }
91+
/// }
92+
/// // Fallback on using division.
93+
/// return data.map { $0 / divisor }
94+
/// }
95+
/// ```
7296
var reciprocal: Self? { get }
7397
}
7498

Sources/RealModule/Real.swift

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,47 @@ extension Real {
103103
/// if it is representable.
104104
///
105105
/// If `a` if finite and nonzero, and `1/a` overflows or underflows,
106-
/// then `a.reciprocal` is `nil`. Otherwise, `a.reciprcoal` is `1/a`.
106+
/// then `a.reciprocal` is `nil`. Otherwise, `a.reciprocal` is `1/a`.
107+
///
108+
/// If `b.reciprocal` is non-nil, you may be able to replace division by `b`
109+
/// with multiplication by this value. It is not advantageous to do this
110+
/// for an isolated division unless it is a compile-time constant visible
111+
/// to the compiler, but if you are dividing many values by a single
112+
/// denominator, this will often be a significant performance win.
113+
///
114+
/// A typical use case looks something like this:
115+
/// ```
116+
/// func divide<T: Real>(data: [T], by divisor: T) -> [T] {
117+
/// // If divisor is well-scaled, multiply by reciprocal.
118+
/// if let recip = divisor.reciprocal {
119+
/// return data.map { $0 * recip }
120+
/// }
121+
/// // Fallback on using division.
122+
/// return data.map { $0 / divisor }
123+
/// }
124+
/// ```
125+
///
126+
/// Error Bounds:
127+
/// -
128+
/// Multiplying by the reciprocal instead of dividing will slightly
129+
/// perturb results. For example `5.0 / 3` is 1.6666666666666667, but
130+
/// `5.0 * 3.reciprocal!` is 1.6666666666666665.
131+
///
132+
/// The error of a normal division is bounded by half an ulp of the
133+
/// result; we can derive a quick error bound for multiplication by
134+
/// the real reciprocal (when it exists) as follows (I will use circle
135+
/// operators to denote real-number arithmetic, and normal operators
136+
/// for floating-point arithmetic):
137+
///
138+
/// a * b.reciprocal! = a * (1/b)
139+
/// = a * (1 ⊘ b)(1 + δ₁)
140+
/// = (a ⊘ b)(1 + δ₁)(1 + δ₂)
141+
/// = (a ⊘ b)(1 + δ₁ + δ₂ + δ₁δ₂)
142+
///
143+
/// where 0 < δᵢ <= ulpOfOne/2. This gives a roughly 1-ulp error,
144+
/// about twice the error bound we get using division. For most
145+
/// purposes this is an acceptable error, but if you need to match
146+
/// results obtained using division, you should not use this.
107147
@inlinable
108148
public var reciprocal: Self? {
109149
let recip = 1/self

0 commit comments

Comments
 (0)