Skip to content

Commit

Permalink
Merge pull request #193 from stephentyrone/cherry-pick-doc-changes-fr…
Browse files Browse the repository at this point in the history
…om-main

Cherry pick doc changes from main
  • Loading branch information
stephentyrone authored Aug 27, 2021
2 parents 03c5840 + bcde4bc commit 79f6025
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 10 deletions.
30 changes: 27 additions & 3 deletions Sources/ComplexModule/Arithmetic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,31 @@ extension Complex: AdditiveArithmetic {
// and turn these into operators if/when we have it.
// (https://github.com/apple/swift-numerics/issues/12)
extension Complex {
/// `self` scaled by `a`.
@usableFromInline @_transparent
internal func multiplied(by a: RealType) -> Complex {
// This can be viewed in two different ways, which are mathematically
// equivalent: either we are computing `self * Complex(a)` (i.e.
// converting `a` to be a complex value, and then using the complex
// multiplication) or we are using the scalar product of the vector
// space structure: `Complex(a*real, a*imaginary)`.
//
// Although these two interpretations are _mathematically_ equivalent,
// they will generate different representations of the point at
// infinity in general. For example, suppose `self` is represented by
// `(infinity, 0)`. Then `self * Complex(1)` would evaluate as
// `(1*infinity - 0*0, 0*infinity + 1*0) = (infinity, nan)`, but
// the vector space interpretation produces `(infinity, 0)`. This does
// not matter much, because these are two representations of the same
// semantic value, but note that one requires four multiplies and two
// additions, while the one we use requires only two real multiplications.
Complex(x*a, y*a)
}

/// `self` unscaled by `a`.
@usableFromInline @_transparent
internal func divided(by a: RealType) -> Complex {
// See implementation notes for `multiplied` above.
Complex(x/a, y/a)
}
}
Expand Down Expand Up @@ -145,19 +163,25 @@ extension Complex: AlgebraicField {
/// If z.reciprocal is non-nil, you can safely replace division by z with
/// multiplication by this value. It is not advantageous to do this for an
/// isolated division, but if you are dividing many values by a single
/// denominator, this will often be a significant performance win.
/// denominator, this may sometimes be a significant performance win.
///
/// Typical use looks like this:
/// A typical use case looks something like this:
/// ```
/// func divide<T: Real>(data: [Complex<T>], by divisor: Complex<T>) -> [Complex<T>] {
/// // If divisor is well-scaled, use multiply by reciprocal.
/// // If divisor is well-scaled, multiply by reciprocal.
/// if let recip = divisor.reciprocal {
/// return data.map { $0 * recip }
/// }
/// // Fallback on using division.
/// return data.map { $0 / divisor }
/// }
/// ```
///
/// Error Bounds:
///
/// Unlike real types, when working with complex types, multiplying by the
/// reciprocal instead of dividing cannot change the result. If the
/// reciprocal is non-nil, the two computations are always equivalent.
@inlinable
public var reciprocal: Complex? {
let recip = 1/self
Expand Down
29 changes: 24 additions & 5 deletions Sources/RealModule/AlgebraicField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,6 @@ public protocol AlgebraicField: SignedNumeric {
/// The (approximate) reciprocal (multiplicative inverse) of this number,
/// if it is representable.
///
/// If reciprocal is non-nil, you can replace division by self with
/// multiplication by reciprocal and either get exact the same result
/// (for finite fields) or approximately the same result up to a typical
/// rounding error (for floating-point formats).
///
/// If self is zero and the type has no representation for infinity (as
/// in a typical finite field implementation), or if a reciprocal would
/// overflow or underflow such that it cannot be accurately represented,
Expand All @@ -69,6 +64,30 @@ public protocol AlgebraicField: SignedNumeric {
/// Note that `.zero.reciprocal`, somewhat surprisingly, is *not* nil
/// for `Real` or `Complex` types, because these types have an
/// `.infinity` value that acts as the reciprocal of `.zero`.
///
/// If `x.reciprocal` is non-nil, you may be able to replace division by `x`
/// with multiplication by this value. It is not advantageous to do this
/// for an isolated division unless it is a compile-time constant visible
/// to the compiler, but if you are dividing many values by a single
/// denominator, this will often be a significant performance win.
///
/// Note that this will slightly perturb results for some fields with
/// approximate arithmetic, such as real types--using a normal division
/// is generally more accurate--but no catastrophic loss of accuracy will
/// result. For fields with exact arithmetic, or for the Complex types,
/// the results are identical.
///
/// A typical use case looks something like this:
/// ```
/// func divide<T: AlgebraicField>(data: [T], by divisor: T) -> [T] {
/// // If divisor is well-scaled, multiply by reciprocal.
/// if let recip = divisor.reciprocal {
/// return data.map { $0 * recip }
/// }
/// // Fallback on using division.
/// return data.map { $0 / divisor }
/// }
/// ```
var reciprocal: Self? { get }
}

Expand Down
46 changes: 44 additions & 2 deletions Sources/RealModule/Real.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,50 @@ extension Real {
/// The (approximate) reciprocal (multiplicative inverse) of this number,
/// if it is representable.
///
/// If `a` if finite and nonzero, and `1/a` overflows or underflows,
/// then `a.reciprocal` is `nil`. Otherwise, `a.reciprcoal` is `1/a`.
/// If `x` if finite and nonzero, and `1/x` overflows or underflows,
/// then `x.reciprocal` is `nil`. Otherwise, `a.reciprocal` is `1/x`.
///
/// If `x.reciprocal` is non-nil, you may be able to replace division by `x`
/// with multiplication by this value. It is not advantageous to do this
/// for an isolated division unless it is a compile-time constant visible
/// to the compiler, but if you are dividing many values by a single
/// denominator, this will often be a significant performance win.
///
/// A typical use case looks something like this:
/// ```
/// func divide<T: Real>(data: [T], by divisor: T) -> [T] {
/// // If divisor is well-scaled, multiply by reciprocal.
/// if let recip = divisor.reciprocal {
/// return data.map { $0 * recip }
/// }
/// // Fallback on using division.
/// return data.map { $0 / divisor }
/// }
/// ```
///
/// Error Bounds:
///
/// Multiplying by the reciprocal instead of dividing will slightly
/// perturb results. For example `5.0 / 3` is 1.6666666666666667, but
/// `5.0 * 3.reciprocal!` is 1.6666666666666665.
///
/// The error of a normal division is bounded by half an ulp of the
/// result; we can derive a quick error bound for multiplication by
/// the real reciprocal (when it exists) as follows (I will use circle
/// operators to denote real-number arithmetic, and normal operators
/// for floating-point arithmetic):
///
/// ```
/// a * b.reciprocal! = a * (1/b)
/// = a * (1 ⊘ b)(1 + δ₁)
/// = (a ⊘ b)(1 + δ₁)(1 + δ₂)
/// = (a ⊘ b)(1 + δ₁ + δ₂ + δ₁δ₂)
/// ```
///
/// where `0 < δᵢ <= ulpOfOne/2`. This gives a roughly 1-ulp error,
/// about twice the error bound we get using division. For most
/// purposes this is an acceptable error, but if you need to match
/// results obtained using division, you should not use this.
@inlinable
public var reciprocal: Self? {
let recip = 1/self
Expand Down

0 comments on commit 79f6025

Please sign in to comment.