@@ -75,11 +75,57 @@ final class ElementaryFunctionTests: XCTestCase {
75
75
}
76
76
}
77
77
78
+ func testExpMinusOne< T: Real & FixedWidthFloatingPoint & SIMDScalar > ( _ type: T . Type ) {
79
+ // expMinusOne(0) = 0
80
+ XCTAssertEqual ( 0 , Quaternion< T> . expMinusOne( Quaternion ( real: 0 , imaginary: 0 , 0 , 0 ) ) )
81
+ XCTAssertEqual ( 0 , Quaternion< T> . expMinusOne( Quaternion ( real: - 0 , imaginary: 0 , 0 , 0 ) ) )
82
+ XCTAssertEqual ( 0 , Quaternion< T> . expMinusOne( Quaternion ( real: - 0 , imaginary: - 0 , - 0 , - 0 ) ) )
83
+ XCTAssertEqual ( 0 , Quaternion< T> . expMinusOne( Quaternion ( real: 0 , imaginary: - 0 , - 0 , - 0 ) ) )
84
+ // In general, expMinusOne(Quaternion(r,0,0,0)) should be expMinusOne(r),
85
+ // but that breaks down when r is infinity or NaN, because we want all non-
86
+ // finite Quaternion values to be semantically a single point at infinity.
87
+ // This is fine for most inputs, but expMinusOne(Quaternion(-.infinity,0,0,0))
88
+ // would produce 0 if we evaluated it in the usual way.
89
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: . infinity, imaginary: . zero) ) . isFinite)
90
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: . infinity, imaginary: . infinity) ) . isFinite)
91
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: 0 , imaginary: . infinity) ) . isFinite)
92
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: - . infinity, imaginary: . infinity) ) . isFinite)
93
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: - . infinity, imaginary: . zero) ) . isFinite)
94
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: - . infinity, imaginary: - . infinity) ) . isFinite)
95
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: 0 , imaginary: - . infinity) ) . isFinite)
96
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: . infinity, imaginary: - . infinity) ) . isFinite)
97
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: . nan, imaginary: . nan) ) . isFinite)
98
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: . infinity, imaginary: . nan) ) . isFinite)
99
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: . nan, imaginary: . infinity) ) . isFinite)
100
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: - . infinity, imaginary: . nan) ) . isFinite)
101
+ XCTAssertFalse ( Quaternion < T > . expMinusOne ( Quaternion ( real: . nan, imaginary: - . infinity) ) . isFinite)
102
+ // Near-overflow test, same as exp() above.
103
+ let x = T . log ( . greatestFiniteMagnitude) + T. log ( 9 / 8 )
104
+ let huge = Quaternion< T> . expMinusOne( Quaternion ( real: x, imaginary: SIMD3 ( . pi/ 4 , 0 , 0 ) ) )
105
+ let mag = T . greatestFiniteMagnitude/ T. sqrt ( 2 ) * ( 9 / 8 )
106
+ XCTAssert ( huge. real. isApproximatelyEqual ( to: mag) )
107
+ XCTAssert ( huge. imaginary. x. isApproximatelyEqual ( to: mag) )
108
+ XCTAssertEqual ( huge. imaginary. y, . zero)
109
+ XCTAssertEqual ( huge. imaginary. z, . zero)
110
+ // For small values, expMinusOne should be approximately the identity.
111
+ var g = SystemRandomNumberGenerator ( )
112
+ let small = T . ulpOfOne
113
+ for _ in 0 ..< 100 {
114
+ let q = Quaternion < T > (
115
+ real: T . random ( in: - small ... small, using: & g) ,
116
+ imaginary: SIMD3 ( repeating: T . random ( in: - small ... small, using: & g) )
117
+ )
118
+ XCTAssert ( q. isApproximatelyEqual ( to: Quaternion . expMinusOne ( q) , relativeTolerance: 16 * . ulpOfOne) )
119
+ }
120
+ }
121
+
78
122
func testFloat( ) {
79
123
testExp ( Float32 . self)
124
+ testExpMinusOne ( Float32 . self)
80
125
}
81
126
82
127
func testDouble( ) {
83
128
testExp ( Float64 . self)
129
+ testExpMinusOne ( Float64 . self)
84
130
}
85
131
}
0 commit comments