@@ -291,30 +291,35 @@ final class TransformationTests: XCTestCase {
291
291
testActOnVectorRandom ( Float64 . self)
292
292
}
293
293
294
- func testActOnVectorEdgeCase< T: Real & ExpressibleByFloatLiteral & SIMDScalar > ( _ type: T . Type ) {
294
+ func testActOnVectorEdgeCase< T: Real & SIMDScalar > ( _ type: T . Type ) {
295
295
296
296
/// Test for zero, infinity
297
297
let q = Quaternion ( angle: . pi, axis: SIMD3 ( 1 , 0 , 0 ) )
298
298
XCTAssertEqual ( q. act ( on: . zero) , . zero)
299
299
XCTAssertEqual ( q. act ( on: - . zero) , . zero)
300
+ XCTAssertEqual ( q. act ( on: . nan ) , SIMD3 ( repeating: . infinity) )
300
301
XCTAssertEqual ( q. act ( on: . infinity) , SIMD3 ( repeating: . infinity) )
301
302
XCTAssertEqual ( q. act ( on: - . infinity) , SIMD3 ( repeating: . infinity) )
303
+ }
302
304
303
- // Rotate a vector with a value close to greatestFiniteMagnitude
304
- // in all lanes.
305
- // A vector this close to the bounds should not hit infinity when it
306
- // is rotate by a perpendicular axis with an angle that is a multiple of π
305
+ func testActOnVectorEdgeCase ( ) {
306
+ testActOnVectorEdgeCase ( Float32 . self )
307
+ testActOnVectorEdgeCase ( Float64 . self )
308
+ }
307
309
308
- // An axis perpendicular to the vector, so all lanes are changing equally
309
- let axis = SIMD3 < T > ( 1 , 0 , - 1 ) / . sqrt( 2 )
310
- // Create a value (somewhat) close to .greatestFiniteMagnitude
310
+ func testActOnVectorOverflow< T: Real & ExpressibleByFloatLiteral & SIMDScalar > ( _ type: T . Type ) {
311
+ // Create a vector (somewhat) close to greatestFiniteMagnitude on all lanes.
312
+ // We can not use greatestFiniteMagnitude here to test the careful rotation
313
+ // path, as we lose some precision in the process and it will overflow after
314
+ // rescaling the vector.
311
315
let scalar = T (
312
316
sign: . plus, exponent: T . greatestFiniteMagnitude. exponent,
313
317
significand: 1.999999
314
318
)
315
-
316
319
let closeToBounds = SIMD3 < T > ( repeating: scalar)
317
320
321
+ // An axis perpendicular to the vector, so all lanes change equally
322
+ let axis = SIMD3 < T > ( 1 , 0 , - 1 ) / . sqrt( 2 )
318
323
// Perform a 180° rotation on all components
319
324
let pi = Quaternion ( angle: . pi, axis: axis) . act ( on: closeToBounds)
320
325
// Must be finite after the rotation
@@ -324,21 +329,32 @@ final class TransformationTests: XCTestCase {
324
329
XCTAssertTrue ( closeEnough ( pi. x, - scalar, ulps: 4 ) )
325
330
XCTAssertTrue ( closeEnough ( pi. y, - scalar, ulps: 4 ) )
326
331
XCTAssertTrue ( closeEnough ( pi. z, - scalar, ulps: 4 ) )
332
+ }
327
333
328
- // Perform a 360° rotation on all components
329
- let twoPi = Quaternion ( angle: 2 * . pi, axis: axis) . act ( on: closeToBounds)
330
- // Must still be finite after the process
331
- XCTAssertTrue ( twoPi. x. isFinite)
332
- XCTAssertTrue ( twoPi. y. isFinite)
333
- XCTAssertTrue ( twoPi. z. isFinite)
334
- XCTAssertTrue ( closeEnough ( twoPi. x, scalar, ulps: 8 ) )
335
- XCTAssertTrue ( closeEnough ( twoPi. y, scalar, ulps: 8 ) )
336
- XCTAssertTrue ( closeEnough ( twoPi. z, scalar, ulps: 8 ) )
334
+ func testActOnVectorOverflow( ) {
335
+ testActOnVectorOverflow ( Float32 . self)
336
+ testActOnVectorOverflow ( Float64 . self)
337
337
}
338
338
339
- func testActOnVectorEdgeCase( ) {
340
- testActOnVectorEdgeCase ( Float32 . self)
341
- testActOnVectorEdgeCase ( Float64 . self)
339
+ func testActOnVectorUnderflow< T: Real & ExpressibleByFloatLiteral & SIMDScalar > ( _ type: T . Type ) {
340
+ let scalar = T . leastNormalMagnitude
341
+ let closeToZero = SIMD3 < T > ( repeating: scalar)
342
+ // An axis perpendicular to the vector, so all lanes change equally
343
+ let axis = SIMD3 < T > ( 1 , 0 , - 1 ) / . sqrt( 2 )
344
+ // Perform a 180° rotation on all components
345
+ let pi = Quaternion ( angle: . pi, axis: axis) . act ( on: closeToZero)
346
+ // Must be finite after the rotation
347
+ XCTAssertTrue ( pi. x. isFinite)
348
+ XCTAssertTrue ( pi. y. isFinite)
349
+ XCTAssertTrue ( pi. z. isFinite)
350
+ XCTAssertTrue ( closeEnough ( pi. x, - scalar, ulps: 2 ) )
351
+ XCTAssertTrue ( closeEnough ( pi. y, - scalar, ulps: 2 ) )
352
+ XCTAssertTrue ( closeEnough ( pi. z, - scalar, ulps: 2 ) )
353
+ }
354
+
355
+ func testActOnVectorUnderflow( ) {
356
+ testActOnVectorUnderflow ( Float32 . self)
357
+ testActOnVectorUnderflow ( Float64 . self)
342
358
}
343
359
}
344
360
0 commit comments