diff --git a/Sources/ComplexModule/Complex+AdditiveArithmetic.swift b/Sources/ComplexModule/Complex+AdditiveArithmetic.swift index 58f59fe1..0806fb4f 100644 --- a/Sources/ComplexModule/Complex+AdditiveArithmetic.swift +++ b/Sources/ComplexModule/Complex+AdditiveArithmetic.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019-2021 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -14,7 +14,7 @@ import RealModule extension Complex: AdditiveArithmetic { /// The additive identity, with real and imaginary parts both zero. /// - /// See also: `one`, `i`, `infinity` + /// See also: ``one``, ``i``, ``infinity`` @_transparent public static var zero: Complex { Complex(0, 0) diff --git a/Sources/ComplexModule/Complex+AlgebraicField.swift b/Sources/ComplexModule/Complex+AlgebraicField.swift index 42e847ad..c6f4cb85 100644 --- a/Sources/ComplexModule/Complex+AlgebraicField.swift +++ b/Sources/ComplexModule/Complex+AlgebraicField.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019-2024 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -13,6 +13,8 @@ import RealModule extension Complex: AlgebraicField { /// The multiplicative identity `1 + 0i`. + /// + /// See also: ``zero``, ``i``, ``infinity`` @_transparent public static var one: Complex { Complex(1, 0) diff --git a/Sources/ComplexModule/Complex+Codable.swift b/Sources/ComplexModule/Complex+Codable.swift index 9be3e7d9..f7f094f7 100644 --- a/Sources/ComplexModule/Complex+Codable.swift +++ b/Sources/ComplexModule/Complex+Codable.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information diff --git a/Sources/ComplexModule/Complex+ElementaryFunctions.swift b/Sources/ComplexModule/Complex+ElementaryFunctions.swift index 049dad05..38c8cdce 100644 --- a/Sources/ComplexModule/Complex+ElementaryFunctions.swift +++ b/Sources/ComplexModule/Complex+ElementaryFunctions.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019-2020 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information diff --git a/Sources/ComplexModule/Complex+Hashable.swift b/Sources/ComplexModule/Complex+Hashable.swift index f2daf56a..91e43618 100644 --- a/Sources/ComplexModule/Complex+Hashable.swift +++ b/Sources/ComplexModule/Complex+Hashable.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information diff --git a/Sources/ComplexModule/Complex+IntegerLiteral.swift b/Sources/ComplexModule/Complex+IntegerLiteral.swift index fadb8fc6..07126fc7 100644 --- a/Sources/ComplexModule/Complex+IntegerLiteral.swift +++ b/Sources/ComplexModule/Complex+IntegerLiteral.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information diff --git a/Sources/ComplexModule/Complex+Numeric.swift b/Sources/ComplexModule/Complex+Numeric.swift index 751640c7..5a5d9fbe 100644 --- a/Sources/ComplexModule/Complex+Numeric.swift +++ b/Sources/ComplexModule/Complex+Numeric.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -37,17 +37,20 @@ extension Complex: Numeric { self.init(real, 0) } - /// The ∞-norm of the value (`max(abs(real), abs(imaginary))`). + /// The infinity-norm of the value (a.k.a. "maximum norm" or "Чебышёв + /// [Chebyshev] norm"). /// - /// If you need the Euclidean norm (a.k.a. 2-norm) use the `length` or - /// `lengthSquared` properties instead. + /// Equal to `max(abs(real), abs(imaginary))`. /// - /// Edge cases: - /// - If `z` is not finite, `z.magnitude` is `.infinity`. - /// - If `z` is zero, `z.magnitude` is `0`. - /// - Otherwise, `z.magnitude` is finite and non-zero. + /// If you need to work with the Euclidean norm (a.k.a. 2-norm) instead, + /// use the ``length`` or ``lengthSquared`` properties. If you just need + /// to know "how big" a number is, use this property. + /// + /// **Edge cases:** /// - /// See also `.length` and `.lengthSquared`. + /// - If `z` is not finite, `z.magnitude` is infinity. + /// - If `z` is zero, `z.magnitude` is zero. + /// - Otherwise, `z.magnitude` is finite and non-zero. @_transparent public var magnitude: RealType { guard isFinite else { return .infinity } diff --git a/Sources/ComplexModule/Complex+StringConvertible.swift b/Sources/ComplexModule/Complex+StringConvertible.swift index d1a26f07..85e7f781 100644 --- a/Sources/ComplexModule/Complex+StringConvertible.swift +++ b/Sources/ComplexModule/Complex+StringConvertible.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information diff --git a/Sources/ComplexModule/Complex.swift b/Sources/ComplexModule/Complex.swift index aa3f2609..5929a7ef 100644 --- a/Sources/ComplexModule/Complex.swift +++ b/Sources/ComplexModule/Complex.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -11,39 +11,8 @@ import RealModule -/// A complex number represented by real and imaginary parts. -/// -/// TODO: introductory text on complex numbers -/// -/// Implementation notes: -/// -/// This type does not provide heterogeneous real/complex arithmetic, -/// not even the natural vector-space operations like real * complex. -/// There are two reasons for this choice: first, Swift broadly avoids -/// mixed-type arithmetic when the operation can be adequately expressed -/// by a conversion and homogeneous arithmetic. Second, with the current -/// typechecker rules, it would lead to undesirable ambiguity in common -/// expressions (see README.md for more details). -/// -/// Unlike C's `_Complex` and C++'s `std::complex<>` types, we do not -/// attempt to make meaningful semantic distinctions between different -/// representations of infinity or NaN. Any Complex value with at least -/// one non-finite component is simply "non-finite". In as much as -/// possible, we use the semantics of the point at infinity on the -/// Riemann sphere for such values. This approach simplifies the number of -/// edge cases that need to be considered for multiplication, division, and -/// the elementary functions considerably. -/// -/// `.magnitude` does not return the Euclidean norm; it uses the "infinity -/// norm" (`max(|real|,|imaginary|)`) instead. There are two reasons for this -/// choice: first, it's simply faster to compute on most hardware. Second, -/// there exist values for which the Euclidean norm cannot be represented -/// (consider a number with `.real` and `.imaginary` both equal to -/// `RealType.greatestFiniteMagnitude`; the Euclidean norm would be -/// `.sqrt(2) * .greatestFiniteMagnitude`, which overflows). Using -/// the infinity norm avoids this problem entirely without significant -/// downsides. You can access the Euclidean norm using the `length` -/// property. +// A [complex number](https://en.wikipedia.org/wiki/Complex_number). +// See Documentation.docc/Complex.md for more details. @frozen public struct Complex where RealType: Real { // A note on the `x` and `y` properties @@ -53,11 +22,11 @@ public struct Complex where RealType: Real { // `.real` and `.imaginary` properties, which wrap this storage and // fixup the semantics for non-finite values. - /// The real component of the value. + /// The storage for the real component of the value. @usableFromInline @inline(__always) internal var x: RealType - /// The imaginary part of the value. + /// The storage for the imaginary part of the value. @usableFromInline @inline(__always) internal var y: RealType @@ -95,11 +64,24 @@ extension Complex { set { y = newValue } } + /// The raw representation of the value. + /// + /// Use this when you need the underlying RealType values, + /// without fixup for NaN or infinity. + public var rawStorage: (x: RealType, y: RealType) { + @_transparent + get { (x, y) } + @_transparent + set { (x, y) = newValue } + } + /// The raw representation of the real part of this value. + @available(*, deprecated, message: "Use rawStorage") @_transparent public var _rawX: RealType { x } /// The raw representation of the imaginary part of this value. + @available(*, deprecated, message: "Use rawStorage") @_transparent public var _rawY: RealType { y } } @@ -107,7 +89,7 @@ extension Complex { extension Complex { /// The imaginary unit. /// - /// See also `.zero`, `.one` and `.infinity`. + /// See also ``zero``, ``one`` and ``infinity``. @_transparent public static var i: Complex { Complex(0, 1) @@ -115,7 +97,7 @@ extension Complex { /// The point at infinity. /// - /// See also `.zero`, `.one` and `.i`. + /// See also ``zero``, ``one`` and ``i``. @_transparent public static var infinity: Complex { Complex(.infinity, 0) @@ -125,7 +107,7 @@ extension Complex { /// /// A complex value is finite if neither component is an infinity or nan. /// - /// See also `.isNormal`, `.isSubnormal` and `.isZero`. + /// See also ``isNormal``, ``isSubnormal`` and ``isZero``. @_transparent public var isFinite: Bool { x.isFinite && y.isFinite @@ -138,7 +120,7 @@ extension Complex { /// one of the components is normal if its exponent allows a full-precision /// representation. /// - /// See also `.isFinite`, `.isSubnormal` and `.isZero`. + /// See also ``isFinite``, ``isSubnormal`` and ``isZero``. @_transparent public var isNormal: Bool { isFinite && (x.isNormal || y.isNormal) @@ -150,7 +132,7 @@ extension Complex { /// When the result of a computation is subnormal, underflow has occurred and /// the result generally does not have full precision. /// - /// See also `.isFinite`, `.isNormal` and `.isZero`. + /// See also ``isFinite``, ``isNormal`` and ``isZero``. @_transparent public var isSubnormal: Bool { isFinite && !isNormal && !isZero @@ -161,7 +143,7 @@ extension Complex { /// A complex number is zero if *both* the real and imaginary components /// are zero. /// - /// See also `.isFinite`, `.isNormal` and `isSubnormal`. + /// See also ``isFinite``, ``isNormal`` and ``isSubnormal``. @_transparent public var isZero: Bool { x == 0 && y == 0 @@ -200,7 +182,7 @@ extension Complex { self.init(real, 0) } - /// The complex number with specified imaginary part and zero real part. + /// The complex number with zero real part and specified imaginary part. /// /// Equivalent to `Complex(0, imaginary)`. @inlinable diff --git a/Sources/ComplexModule/Documentation.docc/Complex.md b/Sources/ComplexModule/Documentation.docc/Complex.md new file mode 100644 index 00000000..71984612 --- /dev/null +++ b/Sources/ComplexModule/Documentation.docc/Complex.md @@ -0,0 +1,67 @@ +# ``Complex`` + +A complex number type represented by its real and imaginary parts, and equipped +with the usual arithmetic operators and math functions. + +## Overview + +You can access these Cartesian components using the real and imaginary +properties. + +```swift +let z = Complex(1,-1) // 1 - i +let re = z.real // 1 +let im = z.imaginary // -1 +``` + +All `Complex` numbers with a non-finite component are treated as a single +"point at infinity," with infinite magnitude and indeterminant phase. Thus, +the real and imaginary parts of an infinity are nan. + +```swift +let w = Complex.infinity +w == -w // true +let re = w.real // .nan +let im = w.imag // .nan +``` + +See for more details. + +The ``magnitude`` property of a complex number is the infinity norm of the +value (a.k.a. “maximum norm” or “Чебышёв [Chebyshev] norm”). To get the two +norm (a.k.a. "Euclidean norm"), use the ``length`` property. See + for more details. + +## Topics + +### Real and imaginary parts + +- ``real`` +- ``imaginary`` +- ``rawStorage`` +- ``init(_:_:)`` +- ``init(_:)-5aesj`` +- ``init(imaginary:)`` + +### Phase, length and magnitude + +- ``magnitude`` +- ``length`` +- ``lengthSquared`` +- ``normalized`` +- ``phase`` +- ``polar`` +- ``init(length:phase:)`` + +### Scaling by real numbers +- ``multiplied(by:)`` +- ``divided(by:)`` + +### Complex-specific operations +- ``conjugate`` + +### Classification +- ``isZero`` +- ``isSubnormal`` +- ``isNormal`` +- ``isFinite`` diff --git a/Sources/ComplexModule/Documentation.docc/ComplexModule.md b/Sources/ComplexModule/Documentation.docc/ComplexModule.md new file mode 100644 index 00000000..f77ecd21 --- /dev/null +++ b/Sources/ComplexModule/Documentation.docc/ComplexModule.md @@ -0,0 +1,88 @@ +# ``ComplexModule`` + +Types and operations for working with complex numbers. + +## Overview + +### Representation + +The `Complex` type is generic over an associated `RealType`; complex numbers +are represented as two `RealType` values, the real and imaginary parts of the +number. + +``` +let z = Complex(1, 2) +let re = z.real +let im = z.imaginary +``` + +### Memory layout + +A `Complex` value is stored as two `RealType` values arranged consecutively +in memory. Thus it has the same memory layout as: +- A Fortran complex value built on the corresponding real type (as used +by BLAS and LAPACK). +- A C struct with real and imaginary parts and nothing else (as used by +computational libraries predating C99). +- A C99 `_Complex` value built on the corresponding real type. +- A C++ `std::complex` value built on the corresponding real type. +Functions taking complex arguments in these other languages are not +automatically converted on import, but you can safely write shims that +map them into Swift types by converting pointers. + +### Real-Complex arithmetic + +Because the real numbers are a subset of the complex numbers, many +languages support arithmetic with mixed real and complex operands. +For example, C allows the following: + +```c +#include +double r = 1; +double complex z = CMPLX(0, 2); // 2i +double complex w = r + z; // 1 + 2i +``` + +The `Complex` type does not provide such mixed operators: + +```swift +let r = 1.0 +let z = Complex(imaginary: 2.0) +let w = r + z // error: binary operator '+' cannot be applied to operands of type 'Double' and 'Complex' +``` + +In order to write the example from C above in Swift, you have to perform an +explicit conversion: + +```swift +let r = 1.0 +let z = Complex(imaginary: 2.0) +let w = Complex(r) + z // OK +``` + +There are two reasons for this choice. Most importantly, Swift generally avoids +mixed-type arithmetic. Second, if we _did_ provide such heterogeneous operators, +it would lead to undesirable behavior in common expressions when combined with +literal type inference. Consider the following example: + +```swift +let a: Double = 1 +let b = 2*a +``` + +`b` ought to have type `Double`, but if we did have a Complex-by-Real `*` +operation, `2*a` would either be ambiguous (if there were no type context), +or be inferred to have type `Complex` (if the expression appeared +in the context of an extension defined on `Complex`). + +Note that we _do_ provide heterogeneous multiplication and division by a real +value, spelled as ``Complex/divided(by:)`` and ``Complex/multiplied(by:)`` +to avoid ambiguity. + +```swift +let z = Complex(1,3) +let w = z.multiplied(by: 2) +``` + +These operations are generally more efficient than converting the scale to +a complex number and then using `*` or `/`. diff --git a/Sources/ComplexModule/Documentation.docc/Infinity.md b/Sources/ComplexModule/Documentation.docc/Infinity.md new file mode 100644 index 00000000..c4c1f081 --- /dev/null +++ b/Sources/ComplexModule/Documentation.docc/Infinity.md @@ -0,0 +1,55 @@ +# Zero and infinity + +Semantics of `Complex` zero and infinity values, and important considerations +when porting code from other languages. + +## Overview + +Unlike C and C++'s complex types, `Complex` does not attempt to make a +semantic distinction between different infinity and NaN values. Any `Complex` +datum with a non-finite component is treated as the "point at infinity" on +the Riemann sphere--a value with infinite magnitude and unspecified phase. + +As a consequence, all values with either component infinite or NaN compare +equal, and hash the same. Similarly, all zero values compare equal and hash +the same. + +### Rationale + +This choice has some drawbacks,¹ but also some significant advantages. +In particular, complex multiplication is the most common operation performed +with a complex type, and one would like to be able to use the usual naive +arithmetic implementation, consisting of four real multiplications and two +real additions: + +``` +(a + bi) * (c + di) = (ac - bd) + (ad + bc)i +``` + +`Complex` can use this implementation, because we do not differentiate between +infinities and NaN values. C and C++, by contrast, cannot use this +implementation by default, because, for example: + +``` +(1 + ∞i) * (0 - 2i) = (1*0 - ∞*(-2)) + (1*(-2) + ∞*0)i + = (0 - ∞) + (-2 + nan)i + = -∞ + nan i +``` + +`Complex` treats this as "infinity", which is the correct result. C and C++ +treat it as a nan value, however, which is incorrect; infinity multiplied +by a non-zero number should be infinity. Thus, C and C++ (by default) must +detect these special cases and fix them up, which makes multiplication a +more computationally expensive operation.² + +### Footnotes: +¹ W. Kahan, Branch Cuts for Complex Elementary Functions, or Much Ado +About Nothing's Sign Bit. In A. Iserles and M.J.D. Powell, editors, +_Proceedings The State of Art in Numerical Analysis_, pages 165–211, 1987. + +² This can be addressed in C programs by use of the `STDC CX_LIMITED_RANGE` +pragma, which instructs the compiler to simply not care about these cases. +Unfortunately, this pragma is not often used in real C or C++ programs +(though it does see some use in _libraries_). Programmers tend to specify +`-ffast-math` or maybe `-ffinite-math-only` instead, which has other +undesirable consequences. diff --git a/Sources/ComplexModule/Documentation.docc/Magnitude.md b/Sources/ComplexModule/Documentation.docc/Magnitude.md new file mode 100644 index 00000000..f1565e34 --- /dev/null +++ b/Sources/ComplexModule/Documentation.docc/Magnitude.md @@ -0,0 +1,144 @@ +# Magnitude and norms + +Introduction to the concept of norm and discussion of the `Complex` type's +`.magnitude` property. + +## Overview + +In mathematics, a *norm* is a function that gives each element of a vector +space a non-negative length.¹ + +Many different norms can be defined on the complex numbers, viewed as a +vector space over the reals. All of these norms have some basic +properties. If we use *‖z‖* to represent any norm of *z*, it must: +- be *subadditive* (a.k.a. satisfy the triangle inequalty): + ‖z + w‖ ≤ ‖z‖ + ‖w‖ for any two complex numbers z and w. +- be *homogeneous* + ‖az‖ = |a|‖z‖ for any real number a and complex number z. +- and be *positive definite* + ‖z‖ is zero if and only if z is zero. + +The three most commonly-used norms are: +- 1-norm ("taxicab norm"): + ‖x + iy‖₁ = |x| + |y| +- 2-norm ("Euclidean norm"): + ‖x + iy‖₂ = √(x² + y²) +- ∞-norm ("maximum norm" or "Чебышёв [Chebyshev] norm")²: + ‖x + iy‖ = max(|x|,|y|) + +> Exercise: +> 1. Check that these properties hold for one of the three norms + that we just defined. (Hint: write z = a+bi and w = c+di, + and use the fact that the absolute value is a norm on the + real numbers, and therefore has the same property). + +The `Complex` type gives special names to two of these norms; `length` +for the 2-norm, and `magnitude` for the ∞-norm. + +### Magnitude: + +The `Numeric` protocol requires us to choose a norm to call `magnitude`, +but does not give guidance as to which one we should pick. The easiest choice +might have been the Euclidean norm; it's the one with which people are most +likely to be familiar. + +However, there are good reasons to make a different choice: +- Computing the Euclidean norm requires special care to avoid spurious + overflow/underflow (see implementation notes for `length` below). The + naive expressions for the taxicab and maximum norm always give the best + answer possible. +- Even when special care is used, the Euclidean and taxicab norms are + not necessarily representable. Both can be infinite even for finite + numbers. + + ```swift + let big = Double.greatestFiniteMagnitude + let z = Complex(big, big) + ``` + + The taxicab norm of `z` would be `big + big`, which overflows; the + Euclidean norm would be `sqrt(2) * big`, which also overflows. + But the maximum norm is always equal to the magnitude of either `real` + or `imaginary`, so it is necessarily representable. +- The ∞-norm is the choice of established computational libraries, like + BLAS and LAPACK. + +For these reasons, the `magnitude` property of `Complex` binds the +maximum norm: + +```swift +Complex(2, 3).magnitude // 3 +Complex(-1, 0.5).magnitude // 1 +``` + +### Length: + +The `length` property of a `Complex` value is its Euclidean norm. + +```swift +Complex(2, 3).length // 3.605551275463989 +Complex(-1, 0.5).length // 1.118033988749895 +``` + +Aside from familiarity, the Euclidean norm has one important property +that the maximum norm lacks: +- it is *multiplicative*: + ‖zw‖₂ = ‖z‖₂‖w‖₂ for any two complex numbers z and w. + +> Exercises: +> 1. Find z and w that show that the maximum norm is not multiplicative. + (i.e. exhibit z and w such that ‖zw‖ ≠ ‖z‖‖w‖.) +> 2. Is the 1-norm multiplicative? + +### Implementation notes: + +The `length` property takes special care to produce an accurate answer, +even when the value is poorly-scaled. The naive expression for `length` +would be `sqrt(x*x + y*y)`, but this can overflow or underflow even when +the final result should be a finite number. + +```swift +// Suppose that length were implemented like this: +extension Complex { + var naiveLength: RealType { + .sqrt(real*real + imaginary*imaginary) + } +} + +// Then taking the length of even a modestly large number: +let z = Complex(1e20, 1e20) +// or small number: +let w = Complex(1e-24, 1e-24) +// would overflow: +z.naiveLength // Inf +// or underflow: +w.naiveLength // 0 +``` + +Instead, `length` is implemented using a two-step algorithm. First we +compute `lengthSquared`, which is `x*x + y*y`. If this is a normal +number (meaning that no overflow or underflow has occured), we can safely +return its square root. Otherwise, we redo the computation with a more +careful computation, which avoids spurious under- or overflow: + +```swift +let z = Complex(1e20, 1e20) +let w = Complex(1e-24, 1e-24) +z.length // 1.41421358E+20 +w.length // 1.41421362E-24 +``` + +### Footnotes: + +¹ Throughout this documentation, "norm" refers to a + [vector norm](https://en.wikipedia.org/wiki/Norm_(mathematics)). + To confuse the matter, there are several similar things also called + "norm" in mathematics. The other one you are most likely to run into + is the [field norm](https://en.wikipedia.org/wiki/Field_norm). + + Field norms are much less common than vector norms, but the C++ + `std::norm` operation implements a field norm. To get the (Euclidean) + vector norm in C++, use `std::abs`. + +² There's no subscript-∞ in unicode, so I write the infinity norm + without the usual subscript. diff --git a/Sources/ComplexModule/Polar.swift b/Sources/ComplexModule/Polar.swift index ecdd878c..e5dabaac 100644 --- a/Sources/ComplexModule/Polar.swift +++ b/Sources/ComplexModule/Polar.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 - 2021 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -12,7 +12,7 @@ import RealModule extension Complex { - /// The Euclidean norm (a.k.a. 2-norm, `sqrt(real*real + imaginary*imaginary)`). + /// The Euclidean norm (a.k.a. 2-norm). /// /// This property takes care to avoid spurious over- or underflow in /// this computation. For example: @@ -26,15 +26,15 @@ extension Complex { /// because the length can be as much as sqrt(2) times larger than /// either component, and thus may not be representable in the real type. /// - /// For most use cases, you can use the cheaper `.magnitude` + /// For most use cases, you can use the cheaper ``magnitude`` /// property (which computes the ∞-norm) instead, which always produces - /// a representable result. + /// a representable result. See for more details. /// /// Edge cases: - /// - If a complex value is not finite, its `.length` is `infinity`. + /// - If a complex value is not finite, its `length` is `infinity`. /// - /// See also `.magnitude`, `.lengthSquared`, `.phase`, `.polar` - /// and `init(r:θ:)`. + /// See also ``lengthSquared``, ``phase``, ``polar`` + /// and ``init(length:phase:)``. @_transparent public var length: RealType { let naive = lengthSquared @@ -42,7 +42,7 @@ extension Complex { return .sqrt(naive) } - // Internal implementation detail of `length`, moving slow path off + // Internal implementation detail of ``length``, moving slow path off // of the inline function. Note that even `carefulLength` can overflow // for finite inputs, but only when the result is outside the range // of representable values. @@ -54,34 +54,31 @@ extension Complex { /// The squared length `(real*real + imaginary*imaginary)`. /// - /// This property is more efficient to compute than `length`, but is + /// This property is more efficient to compute than ``length``, but is /// highly prone to overflow or underflow; for finite values that are /// not well-scaled, `lengthSquared` is often either zero or /// infinity, even when `length` is a finite number. Use this property /// only when you are certain that this value is well-scaled. /// - /// For many cases, `.magnitude` can be used instead, which is similarly + /// For many cases, ``magnitude`` can be used instead, which is similarly /// cheap to compute and always returns a representable value. /// - /// See also `.length` and `.magnitude`. + /// Note that because of how `lengthSquared` is used, it is a primary + /// design goal that it be as fast as possible. Therefore, it does not + /// normalize infinities, and may return either `.infinity` or `.nan` + /// for non-finite values. @_transparent public var lengthSquared: RealType { x*x + y*y } - @available(*, unavailable, renamed: "lengthSquared") - public var unsafeLengthSquared: RealType { lengthSquared } - /// The phase (angle, or "argument"). /// - /// Returns the angle (measured above the real axis) in radians. If + /// - Returns: The angle (measured above the real axis) in radians. If /// the complex value is zero or infinity, the phase is not defined, /// and `nan` is returned. /// - /// Edge cases: - /// - If the complex value is zero or non-finite, phase is `nan`. - /// - /// See also `.length`, `.polar` and `init(r:θ:)`. + /// See also ``length``, ``polar`` and ``init(length:phase:)``. @inlinable public var phase: RealType { guard isFinite && !isZero else { return .nan } @@ -94,7 +91,7 @@ extension Complex { /// - If the complex value is zero or non-finite, phase is `.nan`. /// - If the complex value is non-finite, length is `.infinity`. /// - /// See also: `.length`, `.phase` and `init(r:θ:)`. + /// See also: ``length``, ``phase`` and ``init(length:phase:)``. public var polar: (length: RealType, phase: RealType) { (length, phase) } @@ -117,7 +114,7 @@ extension Complex { /// ``` /// - Otherwise, `θ` must be finite, or a precondition failure occurs. /// - /// See also `.length`, `.phase` and `.polar`. + /// See also ``length``, ``phase`` and ``polar``. @inlinable public init(length: RealType, phase: RealType) { if phase.isFinite { diff --git a/Sources/ComplexModule/Scale.swift b/Sources/ComplexModule/Scale.swift index 57237702..37275cc3 100644 --- a/Sources/ComplexModule/Scale.swift +++ b/Sources/ComplexModule/Scale.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019-2021 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -20,36 +20,22 @@ // what is the type of b? If we don't have a type context, it's ambiguous. // If we have a Complex type context, then b will be inferred to have type // Complex! Obviously, that doesn't help anyone. -// -// TODO: figure out if there's some way to avoid these surprising results -// 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. + /// The result of multiplying this value by the real number `a`. + /// + /// Equivalent to `self * Complex(a)`, but may be computed more efficiently. + @inlinable @inline(__always) + public func multiplied(by a: RealType) -> Complex { Complex(x*a, y*a) } - /// `self` unscaled by `a`. - @usableFromInline @_transparent - internal func divided(by a: RealType) -> Complex { - // See implementation notes for `multiplied` above. + /// The result of dividing this value by the real number `a`. + /// + /// More efficient than `self / Complex(a)`. May not produce exactly the + /// same result, but will always be more accurate if they differ. + @inlinable @inline(__always) + public func divided(by a: RealType) -> Complex { Complex(x/a, y/a) } } diff --git a/Sources/IntegerUtilities/DivideWithRounding.swift b/Sources/IntegerUtilities/DivideWithRounding.swift index caf0b177..1426c008 100644 --- a/Sources/IntegerUtilities/DivideWithRounding.swift +++ b/Sources/IntegerUtilities/DivideWithRounding.swift @@ -12,26 +12,29 @@ extension BinaryInteger { /// `self` divided by `other`, rounding the result according to `rule`. /// - /// The default rounding rule is `.down`, which _is not the same_ as the - /// behavior of the `/` operator from the Swift standard library, but is - /// chosen because it generally produces a more useful remainder. In - /// particular, when `b` is positive, the remainder is always positive. - /// To match the behavior of `/`, use the `.towardZero` rounding mode. + /// By default, this function uses ``RoundingRule/down``, which **is not + /// the same** as the rounding of the `/` operator from the Swift standard + /// library. They agree when the signs of the arguments match, but produce + /// different results when the quotient is negative. This behavior is + /// chosen because it generally produces a more useful remainder; in + /// particular, when the divisor is positive, the remainder is always + /// positive. To match the behavior of `/`, use ``RoundingRule/towardZero``. /// /// Note that the remainder of division is not always representable in an - /// unsigned type if a rounding rule other than `.down`, `.towardZero`, or - /// `.requireExact` is used. For example: - /// - /// let a: UInt = 5 - /// let b: UInt = 3 - /// let q = a.divided(by: b, rounding: .up) // 2 - /// let r = a - b*q // 5 - 3*2 overflows UInt. - /// + /// unsigned type if a rounding rule other than ``RoundingRule/down``, + /// ``RoundingRule/towardZero``, or ``RoundingRule/requireExact`` is + /// used. For example: + /// ```swift + /// let a: UInt = 5 + /// let b: UInt = 3 + /// let q = a.divided(by: b, rounding: .up) // 2 + /// let r = a - b*q // 5 - 3*2 overflows UInt. + /// ``` /// For this reason, there is no `remainder(dividingBy:rounding:)` /// operation defined on `BinaryInteger`. Signed integers do not have - /// this problem, so it is defined on the `SignedInteger` protocol - /// instead, as is an overload of `divided(by:rounding:)` that returns - /// both quotient and remainder. + /// this problem, so the `SignedInteger` protocol is extended with + /// ``SignedInteger/divided(by:rounding:)`` returning both quotient + /// and remainder. @inlinable public func divided( by other: Self, @@ -52,7 +55,7 @@ extension BinaryInteger { // rounded toward zero. // // If we subtract 1 from q, we add other to r to compensate, because: - // + // // self = q*other + r // = (q-1)*other + (r+other) // @@ -151,10 +154,10 @@ extension SignedInteger { /// Divides `self` by `other`, rounding the quotient according to `rule`, /// and returns the remainder. /// - /// The default rounding rule is `.down`, which _is not the same_ as the - /// behavior of the `%` operator from the Swift standard library, but is - /// chosen because it generally produces a more useful remainder. To - /// match the behavior of `%`, use the `.towardZero` rounding mode. + /// The default rounding rule is ``RoundingRule/down``, which _is not the + /// same_ as the behavior of the `%` operator from the Swift standard + /// library, but is chosen because it generally produces a more useful + /// remainder. To match the behavior of `%`, use ``RoundingRule/towardZero``. /// /// - Precondition: `other` cannot be zero. @inlinable @@ -170,10 +173,10 @@ extension SignedInteger { /// Divides `self` by `other`, rounding the quotient according to `rule`, /// and returns both the quotient and remainder. /// - /// The default rounding rule is `.down`, which _is not the same_ as the - /// behavior of the `/` operator from the Swift standard library, but is - /// chosen because it generally produces a more useful remainder. To - /// match the behavior of `/`, use the `.towardZero` rounding mode. + /// The default rounding rule is ``RoundingRule/down``, which _is not the + /// same_ as the behavior of the `%` operator from the Swift standard + /// library, but is chosen because it generally produces a more useful + /// remainder. To match the behavior of `/`, use ``RoundingRule/towardZero``. /// /// Because the default rounding mode does not match Swift's standard /// library, this function is a disfavored overload of `divided(by:)` diff --git a/Sources/IntegerUtilities/SaturatingArithmetic.swift b/Sources/IntegerUtilities/SaturatingArithmetic.swift index 7c001608..94c9e43c 100644 --- a/Sources/IntegerUtilities/SaturatingArithmetic.swift +++ b/Sources/IntegerUtilities/SaturatingArithmetic.swift @@ -31,8 +31,8 @@ extension FixedWidthInteger { /// let c = a.addingWithSaturation(b) /// ``` /// - /// Anytime the "normal addition" `self + other` does not trap, - /// `addingWithSaturation` produces the same result. + /// If the "normal addition" `self + other` does not trap, + /// this method produces the same result. @inlinable public func addingWithSaturation(_ other: Self) -> Self { let (wrapped, overflow) = addingReportingOverflow(other) @@ -54,8 +54,8 @@ extension FixedWidthInteger { /// `a.subtractingWithSaturation(b)`, because `-b` is not representable /// if `b` is the minimum value of a signed type. /// - /// Anytime the "normal subtraction" `self - other` does not trap, - /// `subtractingWithSaturation` produces the same result. + /// If the "normal subtraction" `self - other` does not trap, + /// this method produces the same result. @inlinable public func subtractingWithSaturation(_ other: Self) -> Self { let (wrapped, overflow) = subtractingReportingOverflow(other) @@ -85,8 +85,8 @@ extension FixedWidthInteger { /// let c = a.multipliedWithSaturation(by: b) /// ``` /// - /// Anytime the "normal multiplication" `self * other` does not trap, - /// `multipliedWithSaturation` produces the same result. + /// If the "normal multiplication" `self * other` does not trap, + /// this method produces the same result. @inlinable public func multipliedWithSaturation(by other: Self) -> Self { let (high, low) = multipliedFullWidth(by: other) @@ -100,12 +100,12 @@ extension FixedWidthInteger { /// `self` multiplied by the rational number 2^(`count`), saturated to the /// range `Self.min ... Self.max`, and rounded according to `rule`. /// - /// See `shifted(rightBy:rounding:)` for more discussion of rounding - /// shifts with examples. + /// See `shifted(rightBy:rounding:)`, defined on `BinaryInteger`, for more + /// discussion of rounding shifts with examples. /// /// - Parameters: - /// - leftBy count: the number of bits to shift by. If positive, this is a left-shift, - /// and if negative a right shift. + /// - leftBy count: the number of bits to shift by. If positive, this is + /// a left-shift, and if negative a right shift. /// - rounding rule: the direction in which to round if `count` is negative. @inlinable public func shiftedWithSaturation( diff --git a/Sources/IntegerUtilities/ShiftWithRounding.swift b/Sources/IntegerUtilities/ShiftWithRounding.swift index b83f1e64..cc2f412f 100644 --- a/Sources/IntegerUtilities/ShiftWithRounding.swift +++ b/Sources/IntegerUtilities/ShiftWithRounding.swift @@ -12,8 +12,8 @@ extension BinaryInteger { /// `self` divided by 2^(`count`), rounding the result according to `rule`. /// - /// The default rounding rule is `.down`, which matches the behavior of - /// the `>>` operator from the standard library. + /// The default rounding rule is ``RoundingRule/down``, which matches the + /// behavior of the `>>` operator from the standard library. /// /// Some examples of different rounding rules: /// @@ -138,8 +138,8 @@ extension BinaryInteger { /// `self` divided by 2^(`count`), rounding the result according to `rule`. /// - /// The default rounding rule is `.down`, which matches the behavior of - /// the `>>` operator from the standard library. + /// The default rounding rule is ``RoundingRule/down``, which matches the + /// behavior of the `>>` operator from the standard library. /// /// Some examples of different rounding rules: /// diff --git a/Sources/RealModule/AlgebraicField.swift b/Sources/RealModule/AlgebraicField.swift index 230187b8..d1c276bc 100644 --- a/Sources/RealModule/AlgebraicField.swift +++ b/Sources/RealModule/AlgebraicField.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -32,12 +32,13 @@ /// Because integer multiplication does not form a group; it's commutative and /// associative, but integers do not have multiplicative inverses. /// I.e. if a is any integer other than 1 or -1, there is no integer b such -/// that a*b = 1. The existence of inverses is requried to form a field. +/// that `a*b = 1`. The existence of inverses is requried to form a field. /// -/// If a type `T` conforms to the `Real` protocol, then `T` and `Complex` +/// If a type `T` conforms to the ``Real`` protocol, then `T` and `Complex` /// both conform to `AlgebraicField`. /// -/// See also `Real`, `SignedNumeric`, `Numeric` and `AdditiveArithmetic`. +/// See also Swift's `SignedNumeric`, `Numeric` and `AdditiveArithmetic` +/// protocols. /// /// [field]: https://en.wikipedia.org/wiki/Field_(mathematics) public protocol AlgebraicField: SignedNumeric where Magnitude: AlgebraicField { @@ -90,10 +91,10 @@ public protocol AlgebraicField: SignedNumeric where Magnitude: AlgebraicField { /// ``` var reciprocal: Self? { get } - /// a + b, with the optimizer licensed to reassociate and form FMAs. + /// `a + b`, with the optimizer licensed to reassociate and form FMAs. static func _relaxedAdd(_ a: Self, _ b: Self) -> Self - /// a * b, with the optimizer licensed to reassociate and form FMAs. + /// `a * b`, with the optimizer licensed to reassociate and form FMAs. static func _relaxedMul(_ a: Self, _ b: Self) -> Self } diff --git a/Sources/RealModule/ApproximateEquality.swift b/Sources/RealModule/ApproximateEquality.swift index d9f8128a..8081e377 100644 --- a/Sources/RealModule/ApproximateEquality.swift +++ b/Sources/RealModule/ApproximateEquality.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2020 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2020-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -218,7 +218,7 @@ extension AdditiveArithmetic { /// norm: \.length /// ) /// ``` - /// (if we used the default norm, `.magnitude`, we would be testing if + /// (if we used the default norm, `\.magnitude`, we would be testing if /// `z` were inside a square region instead.) @inlinable public func isApproximatelyEqual( @@ -227,12 +227,6 @@ extension AdditiveArithmetic { relativeTolerance: Magnitude = 0, norm: (Self) -> Magnitude ) -> Bool - // TODO: constraint should really be weaker than FloatingPoint, - // but we need to have `isFinite` for it to work correctly with - // floating-point magnitudes in generic contexts, which is the - // most common case. The fix for this is to lift the isFinite - // requirement to Numeric in the standard library, but that's - // source-breaking, so requires an ABI rumspringa. where Magnitude: FloatingPoint { assert( absoluteTolerance >= 0 && absoluteTolerance.isFinite, diff --git a/Sources/RealModule/AugmentedArithmetic.swift b/Sources/RealModule/AugmentedArithmetic.swift index 5d3c8e6c..bb55c277 100644 --- a/Sources/RealModule/AugmentedArithmetic.swift +++ b/Sources/RealModule/AugmentedArithmetic.swift @@ -2,19 +2,13 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2020-2021 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2020-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // //===----------------------------------------------------------------------===// -/// A namespace for "augmented arithmetic" operations for types conforming to -/// `Real`. -/// -/// Augmented arithmetic refers to a family of algorithms that represent -/// the results of floating-point computations using multiple values such that -/// either the error is minimized or the result is exact. public enum Augmented { } extension Augmented { @@ -45,7 +39,9 @@ extension Augmented { /// - If both `head` and `tail` are normal, then `a * b` is exactly /// equal to `head + tail` when computed as real numbers. @_transparent - public static func product(_ a: T, _ b: T) -> (head: T, tail: T) { + public static func product( + _ a: T, _ b: T + ) -> (head: T, tail: T) { let head = a*b // TODO: consider providing an FMA-less implementation for use when // targeting platforms without hardware FMA support. This works everywhere, @@ -64,11 +60,6 @@ extension Augmented { /// error from that computation rounded to the closest representable /// value. /// - /// Unlike `Augmented.product(a, b)`, the rounding error of a sum can - /// never underflow. - /// - /// This operation is sometimes called ["fastTwoSum"]. - /// /// > Note: /// > `tail` is guaranteed to be the best approximation to the error of /// the sum only if `large.magnitude` >= `small.magnitude`. If this is @@ -77,6 +68,15 @@ extension Augmented { /// how the magnitudes of `a` and `b` compare, you likely want to use /// ``sum(_:_:)`` instead. /// + /// Unlike ``product(_:_:)``, the rounding error of `sum` never underflows. + /// + /// This operation is sometimes called ["fastTwoSum"]. + /// + /// > Note: + /// > Classical fastTwoSum does not work when `radix` is 10. This function + /// will fall back on another algorithm for decimal floating-point types + /// to ensure correct results. + /// /// Edge Cases: /// /// - `head` is always the IEEE 754 sum `a + b`. @@ -90,7 +90,14 @@ extension Augmented { /// /// ["fastTwoSum"]: https://en.wikipedia.org/wiki/2Sum @_transparent - public static func sum(large a: T, small b: T) -> (head: T, tail: T) { + public static func sum( + large a: T, small b: T + ) -> (head: T, tail: T) { + // Fall back on 2Sum if radix != 2. Future implementations might use an + // cheaper algorithm specialized for decimal FP, but must deliver a + // correct result if the preconditions are satisfied. + guard T.radix == 2 else { return sum(a, b) } + // Fast2Sum: let head = a + b let tail = a - head + b return (head, tail) @@ -102,17 +109,18 @@ extension Augmented { /// error from that computation rounded to the closest representable /// value. /// - /// Unlike ``sum(large:small:)``, the magnitude of the summands does not - /// matter. If you know statically that `a.magnitude >= b.magnitude`, you - /// should use ``sum(large:small:)``. If you do not have such a static - /// bound, you should use this function instead. + /// The magnitude of the summands does not change the result of this + /// operation. If you know statically that `a.magnitude >= b.magnitude`, + /// you may want to consider using ``sum(large:small:)`` to get the same + /// result somewhat more efficiently. If you do not have such a static + /// bound, you usually want to use this function instead. /// - /// Unlike ``product(_:_:)``, the rounding error of a sum never underflows. + /// Unlike ``product(_:_:)``, the rounding error of `sum` never underflows. /// /// This operation is sometimes called ["twoSum"]. /// /// - Parameters: - /// - a: One of the summand + /// - a: One of the summands /// - b: The other summand /// /// Edge Cases: @@ -128,7 +136,9 @@ extension Augmented { /// /// ["twoSum"]: https://en.wikipedia.org/wiki/2Sum @_transparent - public static func sum(_ a: T, _ b: T) -> (head: T, tail: T) { + public static func sum( + _ a: T, _ b: T + ) -> (head: T, tail: T) { let head = a + b let x = head - b let y = head - x diff --git a/Sources/RealModule/Documentation.docc/Augmented.md b/Sources/RealModule/Documentation.docc/Augmented.md new file mode 100644 index 00000000..dc60ec04 --- /dev/null +++ b/Sources/RealModule/Documentation.docc/Augmented.md @@ -0,0 +1,37 @@ +# ``Augmented`` + +A namespace for a family of algorithms that compute the results of floating- +point arithmetic using multiple values such that either the error is minimized +or the result is exact. + +## Overview + +Consider multiplying two Doubles. A Double has 53 significand bits, so their +product could be up to 106 bits wide before it is rounded to a Double result. +So up to 53 of those 106 bits will be "lost" in that process: + +```swift +let a = 1.0 + .ulpOfOne // 1 + 2⁻⁵² +let b = 1.0 - .ulpOfOne // 1 - 2⁻⁵² +let c = a * b // 1 - 2⁻¹⁰⁴ before rounding, rounds to 1.0 +``` + +Sometimes it is necessary to preserve some or all of those low-order bits; +maybe a subsequent subtraction cancels most of the high-order bits, and so +the low-order part of the product suddenly becomes significant: + +```swift +let result = 1 - c // exactly zero, but "should be" 2⁻¹⁰⁴ +``` + +Augmented arithmetic is a building-block that library writers can use to +handle cases like this more carefully. For the example above, one might +compute: + +```swift +let (head, tail) = Augmented.product(a,b) +``` + +`head` is then 1.0 and `tail` is -2⁻¹⁰⁴, so no information has been lost. +Of course, the result is now split across two Doubles instead of one, but the +information in `tail` can be carried forward into future computations. diff --git a/Sources/RealModule/Documentation.docc/ElementaryFunctions.md b/Sources/RealModule/Documentation.docc/ElementaryFunctions.md new file mode 100644 index 00000000..68fb2045 --- /dev/null +++ b/Sources/RealModule/Documentation.docc/ElementaryFunctions.md @@ -0,0 +1,68 @@ +# ``ElementaryFunctions`` + + A type that has elementary functions (`sin`, `cos`, etc.) available. + +## Overview + +An ["elementary function"][elfn] is a function built up from powers, roots, +exponentials, logarithms, trigonometric functions (sin, cos, tan) and +their inverses, and the hyperbolic functions (sinh, cosh, tanh) and their +inverses. + +Conformance to this protocol means that all of these building blocks are +available as static functions on the type. + +```swift +let x: Float = 1 +let y = Float.sin(x) // 0.84147096 +``` + +`ElementaryFunctions` conformance implies `AdditiveArithmetic`, so addition +and subtraction and the `zero` property are also available. + +``RealFunctions`` refines this protocol and adds additional functions that +are primarily used with real numbers, such as ``RealFunctions/atan2(y:x:)`` +and ``RealFunctions/exp10(_:)``. + +``Real`` conforms to `RealFunctions` and `FloatingPoint`, and is the +protocol that you will use most often for generic code. + +## Topics + +There are a few families of functions defined by `ElementaryFunctions`: + +### Exponential functions +- ``exp(_:)`` +- ``expMinusOne(_:)`` + +### Logarithmetic functions +- ``log(_:)`` +- ``log(onePlus:)`` + +### Power and root functions: +- ``pow(_:_:)-9imp6`` +- ``pow(_:_:)-2qmul`` +- ``sqrt(_:)`` +- ``root(_:_:)`` + +### Trigonometric functions +- ``cos(_:)`` +- ``sin(_:)`` +- ``tan(_:)`` + +### Inverse trigonometric functions +- ``acos(_:)`` +- ``asin(_:)`` +- ``atan(_:)`` + +### Hyperbolic functions +- ``cosh(_:)`` +- ``sinh(_:)`` +- ``tanh(_:)`` + +### Inverse hyperbolic functions +- ``acosh(_:)`` +- ``asinh(_:)`` +- ``atanh(_:)`` + + [elfn]: http://en.wikipedia.org/wiki/Elementary_function diff --git a/Sources/RealModule/Documentation.docc/RealFunctions.md b/Sources/RealModule/Documentation.docc/RealFunctions.md new file mode 100644 index 00000000..0652d043 --- /dev/null +++ b/Sources/RealModule/Documentation.docc/RealFunctions.md @@ -0,0 +1,27 @@ +# ``RealFunctions`` + +A type that extends ``ElementaryFunctions`` with additional operations that +are primarily used with real numbers. + +## Topics + +### Exponential functions +- ``exp2(_:)`` +- ``exp10(_:)`` + +### Logarithmetic functions +- ``log2(_:)`` +- ``log10(_:)`` + +### Plane geometry +- ``atan2(y:x:)`` +- ``hypot(_:_:)`` + +### Gamma function +- ``gamma(_:)`` +- ``logGamma(_:)`` +- ``signGamma(_:)`` + +### Error function +- ``erf(_:)`` +- ``erfc(_:)`` diff --git a/Sources/RealModule/Documentation.docc/RealModule.md b/Sources/RealModule/Documentation.docc/RealModule.md new file mode 100644 index 00000000..971b8858 --- /dev/null +++ b/Sources/RealModule/Documentation.docc/RealModule.md @@ -0,0 +1,28 @@ +# ``RealModule`` + +Extensions on the Swift standard library that provide functionality for +floating-point types. + +## Overview + +``RealModule`` provides four protocols that extend the standard library's +numeric protocol hierarchy: AlgebraicField, ElementaryFunctions, +RealFunctions, and Real. + +Types conforming to AlgebraicField represent +[fields](https://en.wikipedia.org/wiki/Field_(mathematics)). These are the +mathematical structures that typically form the elements of vectors and +matrices, so this protocol is appropriate for writing generic code to do +linear-algebra-type operations. + +ElementaryFunctions provides bindings for the "math functions": the logarithm +and exponential functions, sine, cosine and tangent as well as their inverses, +and other functions that you may be familiar with from trigonometry and +calculus. RealFunctions refines ElementaryFunctions and provides functions that +are primarily used with the real numbers, such as atan2, erf and gamma, and +the base-2 and -10 logarithm and exponential funtions. + +The Real protocol is a convenient name for the intersection of `FloatingPoint`, +`RealFunctions`, and `AlgebraicField`; this is the protocol that you are most +likely to want to constrain to when writing generic "math" code that works +with floating-point types. diff --git a/Sources/RealModule/Documentation.docc/Relaxed.md b/Sources/RealModule/Documentation.docc/Relaxed.md new file mode 100644 index 00000000..46623c91 --- /dev/null +++ b/Sources/RealModule/Documentation.docc/Relaxed.md @@ -0,0 +1,53 @@ +# ``Relaxed`` + +A namespace for a family of operations that "relax" the usual rules for +floating-point to allow reassociation of arithmetic and FMA formation. + +## Overview + +Because of rounding, and the arithmetic rules for infinity and NaN values, +floating-point addition and multiplication are not associative: + +```swift +let ε = Double.leastNormalMagnitude +let sumLeft = (-1 + 1) + ε // 0 + ε = ε +let sumRight = -1 + (1 + ε) // -1 + 1 = 0 + +let ∞ = Double.infinity +let productLeft = (ε * ε) * ∞ // 0 * ∞ = .nan +let productRight = ε * (ε * ∞) // ε * ∞ = ∞ +``` + +For some algorithms, the distinction between these results is incidental; for +some others it is critical to their correct function. Because of this, +compilers cannot freely change the order of reductions, which prevents some +important optimizations: extraction of instruction-level parallelism and +vectorization. + +If you know that you are in a case where the order of elements being summed +or multiplied is incidental, the Relaxed operations give you a mechanism +to communicate that to the compiler and unlock these optimizations. For +example, consider the following two functions: + +```swift +func sum(array: [Float]) -> Float { + array.reduce(0, +) +} + +func relaxedSum(array: [Float]) -> Float { + array.reduce(0, Relaxed.sum) +} +``` + +when called on an array with 1000 elements in a Release build, `relaxedSum` +is about 8x faster than `sum` on Apple M2, with a similar speedup on Intel +processors, without the need for any unsafe code or flags. + +### multiplyAdd + +In addition to `sum` and `product`, `Relaxed` provides the +``multiplyAdd(_:_:_:)`` operation, which communciates to the compiler that +it is allowed to replace separate multiply and add operations with a single +_fused multiply-add_ instruction if its cost model indicates that it would +be advantageous to do so. When targeting processors that support this +instruction, this may be a significant performance advantage. diff --git a/Sources/RealModule/Double+Real.swift b/Sources/RealModule/Double+Real.swift index 4c6b3417..1a4138dd 100644 --- a/Sources/RealModule/Double+Real.swift +++ b/Sources/RealModule/Double+Real.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information diff --git a/Sources/RealModule/ElementaryFunctions.swift b/Sources/RealModule/ElementaryFunctions.swift index 7e355ec7..d59043b7 100644 --- a/Sources/RealModule/ElementaryFunctions.swift +++ b/Sources/RealModule/ElementaryFunctions.swift @@ -2,62 +2,19 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // //===----------------------------------------------------------------------===// -/// A type that has elementary functions available. -/// -/// An ["elementary function"][elfn] is a function built up from powers, roots, -/// exponentials, logarithms, trigonometric functions (sin, cos, tan) and -/// their inverses, and the hyperbolic functions (sinh, cosh, tanh) and their -/// inverses. -/// -/// Conformance to this protocol means that all of these building blocks are -/// available as static functions on the type. -/// -/// ```swift -/// let x: Float = 1 -/// let y = Float.sin(x) // 0.84147096 -/// ``` -/// -/// There are three broad families of functions defined by -/// `ElementaryFunctions`: -/// - Exponential, trigonometric, and hyperbolic functions: -/// `exp`, `expMinusOne`, `cos`, `sin`, `tan`, `cosh`, `sinh`, and `tanh`. -/// - Logarithmic, inverse trigonometric, and inverse hyperbolic functions: -/// `log`, `log(onePlus:)`, `acos`, `asin`, `atan`, `acosh`, `asinh`, and -/// `atanh`. -/// - Power and root functions: -/// `pow`, `sqrt`, and `root`. -/// -/// `ElementaryFunctions` conformance implies `AdditiveArithmetic`, so addition -/// and subtraction and the `.zero` property are also available. -/// -/// There are two other protocols that you are more likely to want to use -/// directly: -/// -/// `RealFunctions` refines `ElementaryFunctions` and includes -/// additional functions specific to real number types. -/// -/// `Real` conforms to `RealFunctions` and `FloatingPoint`, and is the -/// protocol that you will want to use most often for generic code. -/// -/// See Also: -/// -/// - `RealFunctions` -/// - `Real` -/// -/// [elfn]: http://en.wikipedia.org/wiki/Elementary_function public protocol ElementaryFunctions: AdditiveArithmetic { /// The [exponential function][wiki] e^x whose base `e` is the base of the /// natural logarithm. /// - /// See also `expMinusOne()`, as well as `exp2()` and `exp10()` - /// defined for types conforming to `RealFunctions`. + /// For types that conform to ``RealFunctions`` see + /// ``RealFunctions/exp2(_:)`` and ``RealFunctions/exp10(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Exponential_function static func exp(_ x: Self) -> Self @@ -68,7 +25,7 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// catastrophic cancellation and the result will not have full accuracy. /// The `.expMinusOne(x)` function gives you a means to address this problem. /// - /// As an example, consider the expression `(x + 1)*exp(x) - 1`. When `x` + /// As an example, consider the expression `(x + 1) * .exp(x) - 1`. When `x` /// is smaller than `.ulpOfOne`, this expression evaluates to `0.0`, when it /// should actually round to `2*x`. We can get a full-accuracy result by /// using the following instead: @@ -79,8 +36,8 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// This re-written expression delivers an accurate result for all values /// of `x`, not just for small values. /// - /// See also `exp()`, as well as `exp2()` and `exp10()` defined for types - /// conforming to `RealFunctions`. + /// For types that conform to ``RealFunctions`` see + /// ``RealFunctions/exp2(_:)`` and ``RealFunctions/exp10(_:)``. static func expMinusOne(_ x: Self) -> Self /// The [hyperbolic cosine][wiki] of `x`. @@ -90,7 +47,7 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// 2 /// ``` /// - /// See also `sinh()`, `tanh()` and `acosh()`. + /// See also ``acosh(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Hyperbolic_function static func cosh(_ x: Self) -> Self @@ -102,7 +59,7 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// 2 /// ``` /// - /// See also `cosh()`, `tanh()` and `asinh()`. + /// See also ``asinh(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Hyperbolic_function static func sinh(_ x: Self) -> Self @@ -114,7 +71,7 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// cosh(x) /// ``` /// - /// See also `cosh()`, `sinh()` and `atanh()`. + /// See also ``atanh(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Hyperbolic_function static func tanh(_ x: Self) -> Self @@ -123,7 +80,7 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// /// For real types, `x` may be interpreted as an angle measured in radians. /// - /// See also `sin()`, `tan()` and `acos()`. + /// See also ``acos(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Cosine static func cos(_ x: Self) -> Self @@ -133,7 +90,7 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// /// For real types, `x` may be interpreted as an angle measured in radians. /// - /// See also `cos()`, `tan()` and `asin()`. + /// See also ``asin(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Sine static func sin(_ x: Self) -> Self @@ -142,49 +99,54 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// /// For real types, `x` may be interpreted as an angle measured in radians. /// - /// See also `cos()`, `sin()` and `atan()`, as well as `atan2(y:x:)` for - /// types that conform to `RealFunctions`. + /// See also ``atan(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Tangent static func tan(_ x: Self) -> Self /// The [natural logarithm][wiki] of `x`. /// - /// See also `log(onePlus:)`, as well as `log2()` and `log10()` for types - /// that conform to `RealFunctions`. + /// For types that conform to ``RealFunctions`` see also + /// ``RealFunctions/log2(_:)`` and ``RealFunctions/log10(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Logarithm static func log(_ x: Self) -> Self /// log(1 + x), computed in such a way as to maintain accuracy for small x. /// - /// See also `log()`, as well as `log2()` and `log10()` for types - /// that conform to `RealFunctions`. + /// For types that conform to ``RealFunctions`` see also + /// ``RealFunctions/log2(_:)`` and ``RealFunctions/log10(_:)``. static func log(onePlus x: Self) -> Self /// The [inverse hyperbolic cosine][wiki] of `x`. + /// /// ``` /// cosh(acosh(x)) ≅ x /// ``` - /// See also `asinh()`, `atanh()` and `cosh()`. + /// + /// See also ``cosh(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Inverse_hyperbolic_function static func acosh(_ x: Self) -> Self /// The [inverse hyperbolic sine][wiki] of `x`. + /// /// ``` /// sinh(asinh(x)) ≅ x /// ``` - /// See also `acosh()`, `atanh()` and `sinh()`. + /// + /// See also ``sinh(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Inverse_hyperbolic_function static func asinh(_ x: Self) -> Self /// The [inverse hyperbolic tangent][wiki] of `x`. + /// /// ``` /// tanh(atanh(x)) ≅ x /// ``` - /// See also `acosh()`, `asinh()` and `tanh()`. + /// + /// See also ``tanh(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Inverse_hyperbolic_function static func atanh(_ x: Self) -> Self @@ -193,10 +155,12 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// /// For real types, the result may be interpreted as an angle measured in /// radians. + /// /// ``` /// cos(acos(x)) ≅ x /// ``` - /// See also `asin()`, `atan()` and `cos()`. + /// + /// See also ``cos(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Inverse_trigonometric_functions static func acos(_ x: Self) -> Self @@ -205,10 +169,12 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// /// For real types, the result may be interpreted as an angle measured in /// radians. + /// /// ``` /// sin(asin(x)) ≅ x /// ``` - /// See also `acos()`, `atan()` and `sin()`. + /// + /// See also ``sin(_:)``. /// /// [wiki]: https://en.wikipedia.org/wiki/Inverse_trigonometric_functions static func asin(_ x: Self) -> Self @@ -217,11 +183,14 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// /// For real types, the result may be interpreted as an angle measured in /// radians. + /// /// ``` /// tan(atan(x)) ≅ x /// ``` - /// See also `acos()`, `asin()` and `tan()`, as well as `atan2(y:x:)` for - /// types that conform to `RealArithmetic`. + /// + /// See also ``tan(_:)``. + /// For types that conform to ``RealFunctions``, you will sometimes want + /// to use ``RealFunctions/atan2(y:x:)`` instead. /// /// [wiki]: https://en.wikipedia.org/wiki/Inverse_trigonometric_functions static func atan(_ x: Self) -> Self @@ -233,10 +202,9 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// In particular, this means that if `x` and `y` are both zero, `pow(x,y)` /// is `nan` for real types and `infinity` for complex types, rather than 1. /// - /// There is also a `pow(_:Self,_:Int)` overload, whose behavior is defined - /// in terms of repeated multiplication, and hence returns 1 for this case. - /// - /// See also `sqrt()` and `root()`. + /// There is also + /// , + /// whose behavior is defined in terms of repeated multiplication. static func pow(_ x: Self, _ y: Self) -> Self /// `x` raised to the nth power. @@ -244,19 +212,13 @@ public protocol ElementaryFunctions: AdditiveArithmetic { /// The edge-cases of this function are defined in terms of repeated /// multiplication or division, rather than exp(n log x). In particular, /// `Float.pow(0, 0)` is 1. - /// - /// See also `sqrt()` and `root()`. static func pow(_ x: Self, _ n: Int) -> Self /// The [square root][wiki] of `x`. /// - /// See also `pow()` and `root()`. - /// /// [wiki]: https://en.wikipedia.org/wiki/Square_root static func sqrt(_ x: Self) -> Self /// The nth root of `x`. - /// - /// See also `pow()` and `sqrt()`. static func root(_ x: Self, _ n: Int) -> Self } diff --git a/Sources/RealModule/Real.swift b/Sources/RealModule/Real.swift index ec363df1..2f32b711 100644 --- a/Sources/RealModule/Real.swift +++ b/Sources/RealModule/Real.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -24,23 +24,24 @@ /// } /// ``` /// See also `ElementaryFunctions`, `RealFunctions` and `AlgebraicField`. -public protocol Real: FloatingPoint, RealFunctions, AlgebraicField { -} +public protocol Real: FloatingPoint, RealFunctions, AlgebraicField { } // While `Real` does not provide any additional customization points, // it does allow us to default the implementation of a few operations, // and also provides `signGamma`. extension Real { // Most math libraries do not provide exp10, so we need a default - // implementation. + // implementation. This is not a great one (if the underlying math + // library does not have a sub-ulp accurate pow, this will not get + // exact powers of ten right), but suffices in the short term. @_transparent public static func exp10(_ x: Self) -> Self { - return pow(10, x) + pow(10, x) } /// cos(x) - 1, computed in such a way as to maintain accuracy for small x. /// - /// See also `ElementaryFunctions.expMinusOne()`. + /// See also ``ElementaryFunctions/expMinusOne(_:)``. @_transparent public static func cosMinusOne(_ x: Self) -> Self { let sinxOver2 = sin(x/2) @@ -90,7 +91,7 @@ extension Real { @_transparent public static func sqrt(_ x: Self) -> Self { - return x.squareRoot() + x.squareRoot() } /// The (approximate) reciprocal (multiplicative inverse) of this number, @@ -128,13 +129,13 @@ extension Real { /// 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, + /// ``` + /// 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. diff --git a/Sources/RealModule/RealFunctions.swift b/Sources/RealModule/RealFunctions.swift index 31e6d165..de44752e 100644 --- a/Sources/RealModule/RealFunctions.swift +++ b/Sources/RealModule/RealFunctions.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -10,70 +10,89 @@ //===----------------------------------------------------------------------===// public protocol RealFunctions: ElementaryFunctions { - /// `atan(y/x)`, with sign selected according to the quadrant of `(x, y)`. + /// The signed angle formed in the plane between the vector `(x,y)` and the + /// positive real axis, measured in radians. /// - /// See also `atan()`. + /// The result is in the interval `[-π, π]`. + /// + /// The argument order to `atan2` may be surprising to new programmers. + /// The convention of `y` being the first argument goes back at least to + /// Fortran IV in 1961 and is generally followed in computing with a few + /// notable exceptions (e.g. Mathematica and Excel). This convention was + /// originally chosen because of the mathematical definition of the + /// function: + /// + /// ``` + /// atan2(y,x) = atan(y/x) if x > 0 + /// ``` + /// + /// See also ``ElementaryFunctions/atan(_:)``, as well as the `phase` and + /// `polar` properties defined on the `Complex` type. static func atan2(y: Self, x: Self) -> Self - /// The error function evaluated at `x`. - /// - /// See also `erfc()`. + /// The [error function](https://en.wikipedia.org/wiki/Error_function) + /// evaluated at `x`. static func erf(_ x: Self) -> Self - /// The complimentary error function evaluated at `x`. - /// - /// See also `erf()`. + /// The complimentary [error function](https://en.wikipedia.org/wiki/Error_function) + /// evaluated at `x`. static func erfc(_ x: Self) -> Self - /// 2^x + /// 2 raised to the power x. /// - /// See also `exp()`, `expMinusOne()`, `exp10()`, `log2()` and `pow()`. + /// See also ``log2(_:)``, ``ElementaryFunctions/exp(_:)``, + /// ``ElementaryFunctions/expMinusOne(_:)`` + /// and ``ElementaryFunctions/pow(_:_:)-2qmul``. static func exp2(_ x: Self) -> Self - /// 10^x + /// 10 raised to the power x. /// - /// See also `exp()`, `expMinusOne()`, `exp2()`, `log10()` and `pow()`. + /// See also ``log10(_:)``, ``ElementaryFunctions/exp(_:)``, + /// ``ElementaryFunctions/expMinusOne(_:)`` + /// and ``ElementaryFunctions/pow(_:_:)-2qmul``. static func exp10(_ x: Self) -> Self - /// `sqrt(x*x + y*y)`, computed in a manner that avoids spurious overflow or - /// underflow. + /// The length of the vector `(x,y)`, computed in a manner that avoids + /// spurious overflow or underflow. + /// + /// See also the `length` and `polar` properties defined on the `Complex` + /// type. static func hypot(_ x: Self, _ y: Self) -> Self - /// The gamma function Γ(x). - /// - /// See also `logGamma()` and `signGamma()`. + /// The [gamma function](https://en.wikipedia.org/wiki/Gamma_function) Γ(x). static func gamma(_ x: Self) -> Self /// The base-2 logarithm of `x`. /// - /// See also `exp2()`, `log()`, `log(onePlus:)` and `log10()`. + /// See also ``exp2(_:)``, ``ElementaryFunctions/log(_:)``, + /// and ``ElementaryFunctions/log(onePlus:)``. static func log2(_ x: Self) -> Self /// The base-10 logarithm of `x`. /// - /// See also: `exp10()`, `log()`, `log(onePlus:)` and `log2()`. + /// See also ``exp10(_:)``, ``ElementaryFunctions/log(_:)``, + /// and ``ElementaryFunctions/log(onePlus:)``. static func log10(_ x: Self) -> Self #if !os(Windows) - /// The logarithm of the absolute value of the gamma function, log(|Γ(x)|). + /// The logarithm of the absolute value of the + /// [gamma function](https://en.wikipedia.org/wiki/Gamma_function), + /// log(|Γ(x)|). /// /// Not available on Windows targets. - /// - /// See also `gamma()` and `signGamma()`. static func logGamma(_ x: Self) -> Self - /// The sign of the gamma function, Γ(x). + /// The sign of the + /// [gamma function](https://en.wikipedia.org/wiki/Gamma_function), Γ(x). /// /// For `x >= 0`, `signGamma(x)` is `.plus`. For negative `x`, `signGamma(x)` /// is `.plus` when `x` is an integer, and otherwise it is `.minus` whenever /// `trunc(x)` is even, and `.plus` when `trunc(x)` is odd. /// - /// This function is used together with `logGamma`, which computes the + /// This function is used together with ``logGamma(_:)``, which computes the /// logarithm of the absolute value of Γ(x), to recover the sign information. /// /// Not available on Windows targets. - /// - /// See also `gamma()` and `logGamma()`. static func signGamma(_ x: Self) -> FloatingPointSign #endif } diff --git a/Sources/RealModule/RelaxedArithmetic.swift b/Sources/RealModule/RelaxedArithmetic.swift index e3a5865d..d0821517 100644 --- a/Sources/RealModule/RelaxedArithmetic.swift +++ b/Sources/RealModule/RelaxedArithmetic.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Numerics open source project // -// Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors +// Copyright (c) 2021-2025 Apple Inc. and the Swift Numerics project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -11,12 +11,11 @@ import _NumericsShims -/// A namespace for "relaxed arithmetic" operations for types conforming to -/// `AlgebraicField`. public enum Relaxed { } extension Relaxed { - /// a+b with the optimizer licensed to reassociate expressions and form FMAs. + /// a+b, but grants the optimizer permission to reassociate expressions + /// and form FMAs. /// /// Floating-point addition is not an associative operation, so the Swift /// compiler does not have any flexibility in how it evaluates an expression @@ -45,7 +44,8 @@ extension Relaxed { T._relaxedAdd(a, b) } - /// a*b with the optimizer licensed to reassociate expressions and form FMAs. + /// a*b, but grants the optimizer permission to reassociate expressions + /// and form FMAs. /// /// Floating-point addition and multiplication are not associative operations, /// so the Swift compiler does not have any flexibility in how it evaluates