@@ -29,7 +29,6 @@ import RealModule
29
29
extension Quaternion /*: ElementaryFunctions */ {
30
30
31
31
// MARK: - exp-like functions
32
-
33
32
@inlinable
34
33
public static func exp( _ q: Quaternion ) -> Quaternion {
35
34
// Mathematically, this operation can be expanded in terms of the `Real`
@@ -45,21 +44,17 @@ extension Quaternion/*: ElementaryFunctions */ {
45
44
// less than 1 for most inputs (i.e. `exp(r)` may be infinity when
46
45
// `exp(r) cos(||v||)` would not be.
47
46
guard q. isFinite else { return q }
48
- let θ = q. imaginary. length
49
- let axis = !θ . isZero ? ( q . imaginary / θ ) : . zero
47
+ let ( â , θ ) = q. imaginary. unitAxisAndLength
48
+ let rotation = Quaternion ( halfAngle : θ , unitAxis : â )
50
49
// If real < log(greatestFiniteMagnitude), then exp(real) does not overflow.
51
50
// To protect ourselves against sketchy log or exp implementations in
52
51
// an unknown host library, or slight rounding disagreements between
53
52
// the two, subtract one from the bound for a little safety margin.
54
53
guard q. real < RealType . log ( . greatestFiniteMagnitude) - 1 else {
55
54
let halfScale = RealType . exp ( q. real/ 2 )
56
- let rotation = Quaternion ( halfAngle: θ, unitAxis: axis)
57
55
return rotation. multiplied ( by: halfScale) . multiplied ( by: halfScale)
58
56
}
59
- return Quaternion (
60
- halfAngle: θ,
61
- unitAxis: axis
62
- ) . multiplied ( by: . exp( q. real) )
57
+ return rotation. multiplied ( by: . exp( q. real) )
63
58
}
64
59
65
60
@inlinable
@@ -87,21 +82,20 @@ extension Quaternion/*: ElementaryFunctions */ {
87
82
//
88
83
// See `expMinusOne` on complex numbers for error bounds.
89
84
guard q. isFinite else { return q }
90
- let θ = q. imaginary. length
91
- let axis = !θ. isZero ? ( q. imaginary / θ) : . zero
85
+ let ( â, θ) = q. imaginary. unitAxisAndLength
92
86
// If exp(q) is close to the overflow boundary, we don't need to
93
87
// worry about the "MinusOne" part of this function; we're just
94
88
// computing exp(q). (Even when θ is near a multiple of π/2,
95
89
// it can't be close enough to overcome the scaling from exp(r),
96
90
// so the -1 term is _always_ negligable).
97
91
guard q. real < RealType . log ( . greatestFiniteMagnitude) - 1 else {
98
92
let halfScale = RealType . exp ( q. real/ 2 )
99
- let rotation = Quaternion ( halfAngle: θ, unitAxis: axis )
93
+ let rotation = Quaternion ( halfAngle: θ, unitAxis: â )
100
94
return rotation. multiplied ( by: halfScale) . multiplied ( by: halfScale)
101
95
}
102
96
return Quaternion (
103
97
real: RealType . _mulAdd ( . cos( θ) , . expMinusOne( q. real) , . cosMinusOne( θ) ) ,
104
- imaginary: axis * . exp( q. real) * . sin( θ)
98
+ imaginary: â * . exp( q. real) * . sin( θ)
105
99
)
106
100
}
107
101
@@ -120,23 +114,22 @@ extension Quaternion/*: ElementaryFunctions */ {
120
114
// evaluation of the naive expression, so all we need to be careful
121
115
// about is the behavior near the overflow boundary.
122
116
//
123
- // Fortunately, if |x | >= -log(ulpOfOne), cosh(x ) and sinh(x ) are
124
- // both just exp(|x |)/2, and we already know how to compute that.
117
+ // Fortunately, if |r | >= -log(ulpOfOne), cosh(r ) and sinh(r ) are
118
+ // both just exp(|r |)/2, and we already know how to compute that.
125
119
//
126
120
// This function and sinh should stay in sync; if you make a
127
121
// modification here, you should almost surely make a parallel
128
122
// modification to sinh below.
129
123
guard q. isFinite else { return q }
130
- let θ = q. imaginary. length
131
- let axis = !θ. isZero ? ( q. imaginary / θ) : . zero
124
+ let ( â, θ) = q. imaginary. unitAxisAndLength
132
125
guard q. real. magnitude < - RealType. log ( . ulpOfOne) else {
133
- let rotation = Quaternion ( halfAngle: θ, unitAxis: axis )
126
+ let rotation = Quaternion ( halfAngle: θ, unitAxis: â )
134
127
let firstScale = RealType . exp ( q. real. magnitude/ 2 )
135
128
return rotation. multiplied ( by: firstScale) . multiplied ( by: firstScale/ 2 )
136
129
}
137
130
return Quaternion (
138
131
real: . cosh( q. real) * . cos( θ) ,
139
- imaginary: axis * . sinh( q. real) * . sin( θ)
132
+ imaginary: â * . sinh( q. real) * . sin( θ)
140
133
)
141
134
}
142
135
@@ -150,17 +143,16 @@ extension Quaternion/*: ElementaryFunctions */ {
150
143
// = sinh(r) cos(θ) + (v/θ) cosh(r) sin(θ)
151
144
// ```
152
145
guard q. isFinite else { return q }
153
- let θ = q. imaginary. length
154
- let axis = !θ. isZero ? ( q. imaginary / θ) : . zero
146
+ let ( â, θ) = q. imaginary. unitAxisAndLength
155
147
guard q. real. magnitude < - RealType. log ( . ulpOfOne) else {
156
- let rotation = Quaternion ( halfAngle: θ, unitAxis: axis )
148
+ let rotation = Quaternion ( halfAngle: θ, unitAxis: â )
157
149
let firstScale = RealType . exp ( q. real. magnitude/ 2 )
158
150
let secondScale = RealType ( signOf: q. real, magnitudeOf: firstScale/ 2 )
159
151
return rotation. multiplied ( by: firstScale) . multiplied ( by: secondScale)
160
152
}
161
153
return Quaternion (
162
154
real: . sinh( q. real) * . cos( θ) ,
163
- imaginary: axis * . cosh( q. real) * . sin( θ)
155
+ imaginary: â * . cosh( q. real) * . sin( θ)
164
156
)
165
157
}
166
158
@@ -179,9 +171,9 @@ extension Quaternion/*: ElementaryFunctions */ {
179
171
return Quaternion (
180
172
real: RealType ( signOf: q. real, magnitudeOf: 1 ) ,
181
173
imaginary:
182
- RealType ( signOf: q. imaginary . x, magnitudeOf: 0 ) ,
183
- RealType ( signOf: q. imaginary . y, magnitudeOf: 0 ) ,
184
- RealType ( signOf: q. imaginary . z, magnitudeOf: 0 )
174
+ RealType ( signOf: q. components . x, magnitudeOf: 0 ) ,
175
+ RealType ( signOf: q. components . y, magnitudeOf: 0 ) ,
176
+ RealType ( signOf: q. components . z, magnitudeOf: 0 )
185
177
)
186
178
}
187
179
return sinh ( q) / cosh( q)
@@ -197,17 +189,16 @@ extension Quaternion/*: ElementaryFunctions */ {
197
189
// = cos(r) cosh(θ) - (v/θ) sin(r) sinh(θ)
198
190
// ```
199
191
guard q. isFinite else { return q }
200
- let θ = q. imaginary. length
201
- let axis = !θ. isZero ? ( q. imaginary / θ) : . zero
192
+ let ( â, θ) = q. imaginary. unitAxisAndLength
202
193
guard θ. magnitude < - RealType. log ( . ulpOfOne) else {
203
- let rotation = Quaternion ( halfAngle: q. real, unitAxis: axis )
194
+ let rotation = Quaternion ( halfAngle: q. real, unitAxis: â )
204
195
let firstScale = RealType . exp ( θ. magnitude/ 2 )
205
196
let secondScale = firstScale/ 2
206
197
return rotation. multiplied ( by: firstScale) . multiplied ( by: secondScale)
207
198
}
208
199
return Quaternion (
209
200
real: . cosh( θ) * . cos( q. real) ,
210
- imaginary: - axis * . sinh( θ) * . sin( q. real)
201
+ imaginary: - â * . sinh( θ) * . sin( q. real)
211
202
)
212
203
}
213
204
@@ -221,17 +212,16 @@ extension Quaternion/*: ElementaryFunctions */ {
221
212
// = sin(r) cosh(θ) + (v/θ) cos(r) sinh(θ)
222
213
// ```
223
214
guard q. isFinite else { return q }
224
- let θ = q. imaginary. length
225
- let axis = !θ. isZero ? ( q. imaginary / θ) : . zero
215
+ let ( â, θ) = q. imaginary. unitAxisAndLength
226
216
guard θ. magnitude < - RealType. log ( . ulpOfOne) else {
227
- let rotation = Quaternion ( halfAngle: q. real, unitAxis: axis )
217
+ let rotation = Quaternion ( halfAngle: q. real, unitAxis: â )
228
218
let firstScale = RealType . exp ( θ. magnitude/ 2 )
229
219
let secondScale = RealType ( signOf: θ, magnitudeOf: firstScale/ 2 )
230
220
return rotation. multiplied ( by: firstScale) . multiplied ( by: secondScale)
231
221
}
232
222
return Quaternion (
233
223
real: . cosh( θ) * . sin( q. real) ,
234
- imaginary: axis * . sinh( θ) * . cos( q. real)
224
+ imaginary: â * . sinh( θ) * . cos( q. real)
235
225
)
236
226
}
237
227
@@ -244,17 +234,17 @@ extension Quaternion/*: ElementaryFunctions */ {
244
234
// tan(q) = sin(q) / cos(q)
245
235
// ```
246
236
guard q. isFinite else { return q }
247
- let θ = q. imaginary. length
248
237
// Note that when |θ| is larger than -log(.ulpOfOne),
249
238
// sin(r + v) == ±cos(r + v), so tan(r + v) is just ±1.
250
- guard θ. magnitude < - RealType. log ( . ulpOfOne) else {
239
+ guard q. imaginary. length. magnitude < - RealType. log ( . ulpOfOne) else {
240
+ let r = RealType ( signOf: q. components. w, magnitudeOf: 1 )
251
241
return Quaternion (
252
- real: RealType ( signOf : q . real , magnitudeOf : 1 ) ,
242
+ real: r ,
253
243
imaginary:
254
- RealType ( signOf: q. imaginary . x, magnitudeOf: 0 ) ,
255
- RealType ( signOf: q. imaginary . y, magnitudeOf: 0 ) ,
256
- RealType ( signOf: q. imaginary . z, magnitudeOf: 0 )
257
- ) * Quaternion ( RealType ( signOf : q . real , magnitudeOf : 1 ) )
244
+ RealType ( signOf: q. components . x, magnitudeOf: 0 ) ,
245
+ RealType ( signOf: q. components . y, magnitudeOf: 0 ) ,
246
+ RealType ( signOf: q. components . z, magnitudeOf: 0 )
247
+ ) . multiplied ( by : r )
258
248
}
259
249
return sin ( q) / cos( q)
260
250
}
@@ -276,9 +266,57 @@ extension Quaternion/*: ElementaryFunctions */ {
276
266
return Quaternion ( real: . log( q. length) , imaginary: axis * q. halfAngle)
277
267
}
278
268
279
-
280
- //
281
269
@inlinable
270
+ public static func log( onePlus q: Quaternion ) -> Quaternion {
271
+ // If either |r| or ||v||₁ is bounded away from the origin, we don't need
272
+ // any extra precision, and can just literally compute log(1+z). Note
273
+ // that this includes part of the sphere |1+q| = 1 where log(onePlus:)
274
+ // vanishes (where r <= -0.5), but on this portion of the sphere 1+r
275
+ // is always exact by Sterbenz' lemma, so as long as log( ) produces
276
+ // a good result, log(1+q) will too.
277
+ guard 2 * q. real. magnitude < 1 && q. imaginary. oneNorm < 1 else {
278
+ return log ( . one + q)
279
+ }
280
+ // q is in (±0.5, ±1), so we need to evaluate more carefully.
281
+ // The imaginary part is straightforward:
282
+ let argument = ( . one + q) . halfAngle
283
+ let ( â, _) = q. imaginary. unitAxisAndLength
284
+ let imaginary = â * argument
285
+ // For the real part, we _could_ use the same approach that we do for
286
+ // log( ), but we'd need an extra-precise (1+r)², which can potentially
287
+ // be quite painful to calculate. Instead, we can use an approach that
288
+ // NevinBR suggested on the Swift forums for complex numbers:
289
+ //
290
+ // Re(log 1+q) = (log 1+q + log 1+q̅)/2
291
+ // = log((1+q)(1+q̅)/2
292
+ // = log(1 + q + q̅ + qq̅)/2
293
+ // = log1p((2+r)r + x² + y² + z²)/2
294
+ //
295
+ // So now we need to evaluate (2+r)r + x² + y² + z² accurately. To do this,
296
+ // we employ augmented arithmetic;
297
+ // (2+r)r + x² + y² + z²
298
+ // --↓--
299
+ let rp2 = Augmented . fastTwoSum ( 2 , q. real) // Known that 2 > |r|
300
+ var ( head, δ) = Augmented . twoProdFMA ( q. real, rp2. head)
301
+ var tail = δ
302
+ // head + x² + y² + z²
303
+ // ----↓----
304
+ let x ² = Augmented . twoProdFMA ( q. imaginary. x, q. imaginary. x)
305
+ ( head, δ) = Augmented . twoSum ( head, x ². head)
306
+ tail += ( δ + x ². tail)
307
+ // head + y² + z²
308
+ // ----↓----
309
+ let y ² = Augmented . twoProdFMA ( q. imaginary. y, q. imaginary. y)
310
+ ( head, δ) = Augmented . twoSum ( head, y ². head)
311
+ tail += ( δ + y ². tail)
312
+ // head + z²
313
+ // ----↓----
314
+ let z ² = Augmented . twoProdFMA ( q. imaginary. z, q. imaginary. z)
315
+ ( head, δ) = Augmented . twoSum ( head, z ². head)
316
+ tail += ( δ + z ². tail)
317
+
318
+ let s = ( head + tail) . addingProduct ( q. real, rp2. tail)
319
+ return Quaternion ( real: . log( onePlus: s) / 2 , imaginary: imaginary)
282
320
}
283
321
284
322
//
@@ -341,3 +379,34 @@ extension Quaternion/*: ElementaryFunctions */ {
341
379
return exp ( log ( q) . divided ( by: RealType ( n) ) )
342
380
}
343
381
}
382
+
383
+ extension SIMD3 where Scalar: Real {
384
+
385
+ /// Returns the normalized axis and the length of this vector.
386
+ @usableFromInline @inline ( __always)
387
+ internal var unitAxisAndLength : ( Self , Scalar ) {
388
+ if self == . zero {
389
+ return ( SIMD3 (
390
+ Scalar ( signOf: x, magnitudeOf: 0 ) ,
391
+ Scalar ( signOf: y, magnitudeOf: 0 ) ,
392
+ Scalar ( signOf: z, magnitudeOf: 0 )
393
+ ) , . zero)
394
+ }
395
+ return ( self / length, length)
396
+ }
397
+ }
398
+
399
+ extension Augmented {
400
+
401
+ // TODO: Move to Augmented.swift
402
+ @usableFromInline @_transparent
403
+ internal static func twoSum< T: Real > ( _ a: T , _ b: T ) -> ( head: T , tail: T ) {
404
+ let head = a + b
405
+ let x = head - b
406
+ let y = head - x
407
+ let ax = a - x
408
+ let by = b - y
409
+ let tail = ax + by
410
+ return ( head, tail)
411
+ }
412
+ }
0 commit comments