@@ -291,30 +291,35 @@ final class TransformationTests: XCTestCase {
291291 testActOnVectorRandom ( Float64 . self)
292292 }
293293
294- func testActOnVectorEdgeCase< T: Real & ExpressibleByFloatLiteral & SIMDScalar > ( _ type: T . Type ) {
294+ func testActOnVectorEdgeCase< T: Real & SIMDScalar > ( _ type: T . Type ) {
295295
296296 /// Test for zero, infinity
297297 let q = Quaternion ( angle: . pi, axis: SIMD3 ( 1 , 0 , 0 ) )
298298 XCTAssertEqual ( q. act ( on: . zero) , . zero)
299299 XCTAssertEqual ( q. act ( on: - . zero) , . zero)
300+ XCTAssertEqual ( q. act ( on: . nan ) , SIMD3 ( repeating: . infinity) )
300301 XCTAssertEqual ( q. act ( on: . infinity) , SIMD3 ( repeating: . infinity) )
301302 XCTAssertEqual ( q. act ( on: - . infinity) , SIMD3 ( repeating: . infinity) )
303+ }
302304
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+ }
307309
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.
311315 let scalar = T (
312316 sign: . plus, exponent: T . greatestFiniteMagnitude. exponent,
313317 significand: 1.999999
314318 )
315-
316319 let closeToBounds = SIMD3 < T > ( repeating: scalar)
317320
321+ // An axis perpendicular to the vector, so all lanes change equally
322+ let axis = SIMD3 < T > ( 1 , 0 , - 1 ) / . sqrt( 2 )
318323 // Perform a 180° rotation on all components
319324 let pi = Quaternion ( angle: . pi, axis: axis) . act ( on: closeToBounds)
320325 // Must be finite after the rotation
@@ -324,21 +329,32 @@ final class TransformationTests: XCTestCase {
324329 XCTAssertTrue ( closeEnough ( pi. x, - scalar, ulps: 4 ) )
325330 XCTAssertTrue ( closeEnough ( pi. y, - scalar, ulps: 4 ) )
326331 XCTAssertTrue ( closeEnough ( pi. z, - scalar, ulps: 4 ) )
332+ }
327333
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)
337337 }
338338
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)
342358 }
343359}
344360
0 commit comments