@@ -119,13 +119,103 @@ final class ElementaryFunctionTests: XCTestCase {
119
119
}
120
120
}
121
121
122
+ func testCosh< T: Real & FixedWidthFloatingPoint & SIMDScalar > ( _ type: T . Type ) {
123
+ // cosh(0) = 1
124
+ XCTAssertEqual ( 1 , Quaternion< T> . cosh( Quaternion ( real: 0 , imaginary: 0 , 0 , 0 ) ) )
125
+ XCTAssertEqual ( 1 , Quaternion< T> . cosh( Quaternion ( real: - 0 , imaginary: 0 , 0 , 0 ) ) )
126
+ XCTAssertEqual ( 1 , Quaternion< T> . cosh( Quaternion ( real: - 0 , imaginary: - 0 , - 0 , - 0 ) ) )
127
+ XCTAssertEqual ( 1 , Quaternion< T> . cosh( Quaternion ( real: 0 , imaginary: - 0 , - 0 , - 0 ) ) )
128
+ // cosh is the identity at infinity.
129
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: . infinity, imaginary: . zero) ) . isFinite)
130
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: . infinity, imaginary: . infinity) ) . isFinite)
131
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: 0 , imaginary: . infinity) ) . isFinite)
132
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: - . infinity, imaginary: . infinity) ) . isFinite)
133
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: - . infinity, imaginary: . zero) ) . isFinite)
134
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: - . infinity, imaginary: - . infinity) ) . isFinite)
135
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: 0 , imaginary: - . infinity) ) . isFinite)
136
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: . infinity, imaginary: - . infinity) ) . isFinite)
137
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: . nan, imaginary: . nan) ) . isFinite)
138
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: . infinity, imaginary: . nan) ) . isFinite)
139
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: . nan, imaginary: . infinity) ) . isFinite)
140
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: - . infinity, imaginary: . nan) ) . isFinite)
141
+ XCTAssertFalse ( Quaternion < T > . cosh ( Quaternion ( real: . nan, imaginary: - . infinity) ) . isFinite)
142
+ // Near-overflow test, same as exp() above, but it happens later, because
143
+ // for large x, cosh(x + v) ~ exp(x + v)/2.
144
+ let x = T . log ( . greatestFiniteMagnitude) + T. log ( 18 / 8 )
145
+ let mag = T . greatestFiniteMagnitude/ T. sqrt ( 2 ) * ( 9 / 8 )
146
+ var huge = Quaternion< T> . cosh( Quaternion ( real: x, imaginary: SIMD3 ( . pi/ 4 , 0 , 0 ) ) )
147
+ XCTAssert ( huge. real. isApproximatelyEqual ( to: mag) )
148
+ XCTAssert ( huge. imaginary. x. isApproximatelyEqual ( to: mag) )
149
+ XCTAssertEqual ( huge. imaginary. y, . zero)
150
+ XCTAssertEqual ( huge. imaginary. z, . zero)
151
+ huge = Quaternion< T> . cosh( Quaternion ( real: - x, imaginary: SIMD3 ( . pi/ 4 , 0 , 0 ) ) )
152
+ XCTAssert ( huge. real. isApproximatelyEqual ( to: mag) )
153
+ XCTAssert ( huge. imaginary. x. isApproximatelyEqual ( to: mag) )
154
+ XCTAssertEqual ( huge. imaginary. y, . zero)
155
+ XCTAssertEqual ( huge. imaginary. z, . zero)
156
+ }
157
+
158
+ func testSinh< T: Real & FixedWidthFloatingPoint & SIMDScalar > ( _ type: T . Type ) {
159
+ // sinh(0) = 0
160
+ XCTAssertEqual ( 0 , Quaternion< T> . sinh( Quaternion ( real: 0 , imaginary: 0 , 0 , 0 ) ) )
161
+ XCTAssertEqual ( 0 , Quaternion< T> . sinh( Quaternion ( real: - 0 , imaginary: 0 , 0 , 0 ) ) )
162
+ XCTAssertEqual ( 0 , Quaternion< T> . sinh( Quaternion ( real: - 0 , imaginary: - 0 , - 0 , - 0 ) ) )
163
+ XCTAssertEqual ( 0 , Quaternion< T> . sinh( Quaternion ( real: 0 , imaginary: - 0 , - 0 , - 0 ) ) )
164
+ // sinh is the identity at infinity.
165
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: . infinity, imaginary: . zero) ) . isFinite)
166
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: . infinity, imaginary: . infinity) ) . isFinite)
167
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: 0 , imaginary: . infinity) ) . isFinite)
168
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: - . infinity, imaginary: . infinity) ) . isFinite)
169
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: - . infinity, imaginary: . zero) ) . isFinite)
170
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: - . infinity, imaginary: - . infinity) ) . isFinite)
171
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: 0 , imaginary: - . infinity) ) . isFinite)
172
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: . infinity, imaginary: - . infinity) ) . isFinite)
173
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: . nan, imaginary: . nan) ) . isFinite)
174
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: . infinity, imaginary: . nan) ) . isFinite)
175
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: . nan, imaginary: . infinity) ) . isFinite)
176
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: - . infinity, imaginary: . nan) ) . isFinite)
177
+ XCTAssertFalse ( Quaternion < T > . sinh ( Quaternion ( real: . nan, imaginary: - . infinity) ) . isFinite)
178
+ // Near-overflow test, same as exp() above, but it happens later, because
179
+ // for large x, sinh(x + v) ~ ±exp(x + v)/2.
180
+ let x = T . log ( . greatestFiniteMagnitude) + T. log ( 18 / 8 )
181
+ let mag = T . greatestFiniteMagnitude/ T. sqrt ( 2 ) * ( 9 / 8 )
182
+ var huge = Quaternion< T> . sinh( Quaternion ( real: x, imaginary: SIMD3 ( . pi/ 4 , 0 , 0 ) ) )
183
+ XCTAssert ( huge. real. isApproximatelyEqual ( to: mag) )
184
+ XCTAssert ( huge. imaginary. x. isApproximatelyEqual ( to: mag) )
185
+ XCTAssertEqual ( huge. imaginary. y, . zero)
186
+ XCTAssertEqual ( huge. imaginary. z, . zero)
187
+ huge = Quaternion< T> . sinh( Quaternion ( real: - x, imaginary: SIMD3 ( . pi/ 4 , 0 , 0 ) ) )
188
+ XCTAssert ( huge. real. isApproximatelyEqual ( to: - mag) )
189
+ XCTAssert ( huge. imaginary. x. isApproximatelyEqual ( to: mag) )
190
+ XCTAssertEqual ( huge. imaginary. y, . zero)
191
+ XCTAssertEqual ( huge. imaginary. z, . zero)
192
+ // For randomly-chosen well-scaled finite values, we expect to have
193
+ // cosh² - sinh² ≈ 1. Note that this test would break down due to
194
+ // catastrophic cancellation as we get further away from the origin.
195
+ var g = SystemRandomNumberGenerator ( )
196
+ let values : [ Quaternion < T > ] = ( 0 ..< 1000 ) . map { _ in
197
+ Quaternion (
198
+ real: T . random ( in: - 2 ... 2 , using: & g) ,
199
+ imaginary: SIMD3 ( repeating: T . random ( in: - 2 ... 2 , using: & g) / 3 ) )
200
+ }
201
+ for q in values {
202
+ let c = Quaternion . cosh ( q)
203
+ let s = Quaternion . sinh ( q)
204
+ XCTAssert ( ( c*c - s*s) . isApproximatelyEqual ( to: . one) )
205
+ }
206
+ }
207
+
122
208
func testFloat( ) {
123
209
testExp ( Float32 . self)
124
210
testExpMinusOne ( Float32 . self)
211
+ testCosh ( Float32 . self)
212
+ testSinh ( Float32 . self)
125
213
}
126
214
127
215
func testDouble( ) {
128
216
testExp ( Float64 . self)
129
217
testExpMinusOne ( Float64 . self)
218
+ testCosh ( Float64 . self)
219
+ testSinh ( Float64 . self)
130
220
}
131
221
}
0 commit comments