|
12 | 12 |
|
13 | 13 | import RealModule
|
14 | 14 |
|
| 15 | +// As the following elementary functions algorithms are adoptions of the |
| 16 | +// elementary functions of complex numbers: If you make a modification to either |
| 17 | +// of the following functions, you should almost surely make a parallel |
| 18 | +// modification to the same elementary function of complex numbers (See |
| 19 | +// ElementaryFunctions.swift in ComplexModule). |
15 | 20 | extension Quaternion/*: ElementaryFunctions */ {
|
16 | 21 |
|
17 | 22 | // MARK: - exp-like functions
|
18 | 23 |
|
19 |
| - /// The quaternion exponential function e^q whose base `e` is the base of the |
20 |
| - /// natural logarithm. |
21 |
| - /// |
22 |
| - /// Mathematically, this operation can be expanded in terms of the `Real` |
23 |
| - /// operations `exp`, `cos` and `sin` as follows: |
24 |
| - /// ``` |
25 |
| - /// exp(r + xi + yj + zk) = exp(r + v) = exp(r) exp(v) |
26 |
| - /// = exp(r) (cos(θ) + (v/θ) sin(θ)) where θ = ||v|| |
27 |
| - /// ``` |
28 |
| - /// Note that naive evaluation of this expression in floating-point would be |
29 |
| - /// prone to premature overflow, since `cos` and `sin` both have magnitude |
30 |
| - /// less than 1 for most inputs (i.e. `exp(r)` may be infinity when |
31 |
| - /// `exp(r) cos(θ)` would not be). |
32 |
| - public static func exp(_ q: Quaternion<RealType>) -> Quaternion<RealType> { |
| 24 | + // Mathematically, this operation can be expanded in terms of the `Real` |
| 25 | + // operations `exp`, `cos` and `sin` as follows: |
| 26 | + // ``` |
| 27 | + // exp(r + xi + yj + zk) = exp(r + v) = exp(r) exp(v) |
| 28 | + // = exp(r) (cos(||v||) + (v/||v||) sin(||v||)) |
| 29 | + // ``` |
| 30 | + // Note that naive evaluation of this expression in floating-point would be |
| 31 | + // prone to premature overflow, since `cos` and `sin` both have magnitude |
| 32 | + // less than 1 for most inputs (i.e. `exp(r)` may be infinity when |
| 33 | + // `exp(r) cos(θ)` would not be). |
| 34 | + @inlinable |
| 35 | + public static func exp(_ q: Quaternion) -> Quaternion { |
33 | 36 | guard q.isFinite else { return q }
|
34 | 37 | // For real quaternions we can skip phase and axis calculations
|
35 | 38 | // TODO: Replace q.imaginary == .zero with `q.isReal`
|
36 |
| - let phase = q.imaginary == .zero ? .zero : q.imaginary.length |
37 |
| - let unitAxis = q.imaginary == .zero ? .zero : (q.imaginary / phase) |
| 39 | + let θ = q.imaginary == .zero ? .zero : q.imaginary.length // θ = ||v|| |
| 40 | + let n̂ = q.imaginary == .zero ? .zero : (q.imaginary / θ) // n̂ = v / ||v|| |
38 | 41 | // If real < log(greatestFiniteMagnitude), then exp(q.real) does not overflow.
|
39 | 42 | // To protect ourselves against sketchy log or exp implementations in
|
40 | 43 | // an unknown host library, or slight rounding disagreements between
|
41 | 44 | // the two, subtract one from the bound for a little safety margin.
|
42 | 45 | guard q.real < RealType.log(.greatestFiniteMagnitude) - 1 else {
|
43 | 46 | let halfScale = RealType.exp(q.real/2)
|
44 |
| - let rotation = Quaternion( |
45 |
| - halfAngle: phase, |
46 |
| - unitAxis: unitAxis |
47 |
| - ) |
| 47 | + let rotation = Quaternion(halfAngle: θ, unitAxis: n̂) |
| 48 | + return rotation.multiplied(by: halfScale).multiplied(by: halfScale) |
| 49 | + } |
| 50 | + return Quaternion(halfAngle: θ, unitAxis: n̂).multiplied(by: .exp(q.real)) |
| 51 | + } |
48 | 52 | return rotation.multiplied(by: halfScale).multiplied(by: halfScale)
|
49 | 53 | }
|
50 | 54 | return Quaternion(
|
|
0 commit comments