2
2
//
3
3
// This source file is part of the Swift Numerics open source project
4
4
//
5
- // Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors
5
+ // Copyright (c) 2019-2024 Apple Inc. and the Swift Numerics project authors
6
6
// Licensed under Apache License v2.0 with Runtime Library Exception
7
7
//
8
8
// See https://swift.org/LICENSE.txt for license information
@@ -13,11 +13,23 @@ import XCTest
13
13
import ComplexModule
14
14
import RealModule
15
15
16
+ func ulpsFromInfinity< T: Real > ( _ a: T ) -> T {
17
+ ( . greatestFiniteMagnitude - a) / . greatestFiniteMagnitude. ulp + 1
18
+ }
19
+
16
20
// TODO: improve this to be a general-purpose complex comparison with tolerance
17
21
func relativeError< T> ( _ a: Complex < T > , _ b: Complex < T > ) -> T {
18
22
if a == b { return 0 }
19
- let scale = max ( a. magnitude, b. magnitude, T . leastNormalMagnitude) . ulp
20
- return ( a - b) . magnitude / scale
23
+ if a. isFinite && b. isFinite {
24
+ let scale = max ( a. magnitude, b. magnitude, T . leastNormalMagnitude) . ulp
25
+ return ( a - b) . magnitude / scale
26
+ } else {
27
+ if a. isFinite {
28
+ return ulpsFromInfinity ( a. magnitude)
29
+ } else {
30
+ return ulpsFromInfinity ( b. magnitude)
31
+ }
32
+ }
21
33
}
22
34
23
35
func closeEnough< T: Real > ( _ a: T , _ b: T , ulps allowed: T ) -> Bool {
@@ -29,11 +41,15 @@ func checkMultiply<T>(
29
41
_ a: Complex < T > , _ b: Complex < T > , expected: Complex < T > , ulps allowed: T
30
42
) -> Bool {
31
43
let observed = a*b
44
+ if observed == expected { return false }
45
+ // Even if the expected result is finite, we allow overflow if
46
+ // the two-norm of the expected result overflows.
47
+ if !observed. isFinite && !expected. length. isFinite { return false }
32
48
let rel = relativeError ( observed, expected)
33
- if rel > allowed {
49
+ guard rel <= allowed else {
34
50
print ( " Over-large error in \( a) * \( b) " )
35
51
print ( " Expected: \( expected) \n Observed: \( observed) " )
36
- print ( " Relative error was \( rel) (tolerance: \( allowed) . " )
52
+ print ( " Relative error was \( rel) (tolerance: \( allowed) ) ." )
37
53
return true
38
54
}
39
55
return false
@@ -43,11 +59,15 @@ func checkDivide<T>(
43
59
_ a: Complex < T > , _ b: Complex < T > , expected: Complex < T > , ulps allowed: T
44
60
) -> Bool {
45
61
let observed = a/ b
62
+ if observed == expected { return false }
63
+ // Even if the expected result is finite, we allow overflow if
64
+ // the two-norm of the expected result overflows.
65
+ if !observed. isFinite && !expected. length. isFinite { return false }
46
66
let rel = relativeError ( observed, expected)
47
- if rel > allowed {
67
+ guard rel <= allowed else {
48
68
print ( " Over-large error in \( a) / \( b) " )
49
69
print ( " Expected: \( expected) \n Observed: \( observed) " )
50
- print ( " Relative error was \( rel) (tolerance: \( allowed) . " )
70
+ print ( " Relative error was \( rel) (tolerance: \( allowed) ) ." )
51
71
return true
52
72
}
53
73
return false
@@ -63,7 +83,6 @@ final class ArithmeticTests: XCTestCase {
63
83
func testPolar< T> ( _ type: T . Type )
64
84
where T: BinaryFloatingPoint , T: Real ,
65
85
T. Exponent: FixedWidthInteger , T. RawSignificand: FixedWidthInteger {
66
-
67
86
// In order to support round-tripping from rectangular to polar coordinate
68
87
// systems, as a special case phase can be non-finite when length is
69
88
// either zero or infinity.
@@ -76,10 +95,9 @@ final class ArithmeticTests: XCTestCase {
76
95
XCTAssertEqual ( Complex < T > ( length: - . infinity, phase: . infinity) , . infinity)
77
96
XCTAssertEqual ( Complex < T > ( length: - . infinity, phase: - . infinity) , . infinity)
78
97
XCTAssertEqual ( Complex < T > ( length: - . infinity, phase: . nan ) , . infinity)
79
-
98
+
80
99
let exponentRange =
81
- ( T . leastNormalMagnitude. exponent + T. Exponent ( T . significandBitCount) ) ...
82
- T . greatestFiniteMagnitude. exponent
100
+ T . leastNormalMagnitude. exponent ... T . greatestFiniteMagnitude. exponent
83
101
let inputs = ( 0 ..< 100 ) . map { _ in
84
102
Polar ( length: T (
85
103
sign: . plus,
@@ -136,20 +154,29 @@ final class ArithmeticTests: XCTestCase {
136
154
// Now test multiplication and division using the polar inputs:
137
155
for q in inputs {
138
156
let w = Complex ( length: q. length, phase: q. phase)
139
- let product = Complex ( length: p. length * q. length, phase: p. phase + q. phase)
157
+ var product = Complex ( length: p. length, phase: p. phase + q. phase)
158
+ product. real *= q. length
159
+ product. imaginary *= q. length
140
160
if checkMultiply ( z, w, expected: product, ulps: 16 ) { XCTFail ( ) }
141
- let quotient = Complex ( length: p. length / q. length, phase: p. phase - q. phase)
161
+ var quotient = Complex ( length: p. length, phase: p. phase - q. phase)
162
+ quotient. real /= q. length
163
+ quotient. imaginary /= q. length
142
164
if checkDivide ( z, w, expected: quotient, ulps: 16 ) { XCTFail ( ) }
143
165
}
144
166
}
145
167
}
146
168
147
169
func testPolar( ) {
170
+ #if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) && LONG_TESTS
171
+ if #available( macOS 11 . 0 , iOS 14 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * ) {
172
+ testPolar ( Float16 . self)
173
+ }
174
+ #endif
148
175
testPolar ( Float . self)
149
176
testPolar ( Double . self)
150
- #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android)
177
+ #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android)
151
178
testPolar ( Float80 . self)
152
- #endif
179
+ #endif
153
180
}
154
181
155
182
func testBaudinSmith( ) {
@@ -191,16 +218,38 @@ final class ArithmeticTests: XCTestCase {
191
218
Complex ( 1.02951151789360578e-84 , 6.97145987515076231e-220 ) ) ,
192
219
]
193
220
for test in vectors {
194
- if checkDivide ( test. a, test. b, expected: test. c, ulps: 0.5 ) { XCTFail ( ) }
221
+ if checkDivide ( test. a, test. b, expected: test. c, ulps: 1.0 ) { XCTFail ( ) }
195
222
if checkDivide ( test. a, test. c, expected: test. b, ulps: 1.0 ) { XCTFail ( ) }
196
223
if checkMultiply ( test. b, test. c, expected: test. a, ulps: 1.0 ) { XCTFail ( ) }
197
224
}
198
225
}
199
-
226
+
200
227
func testDivisionByZero( ) {
201
228
XCTAssertFalse ( ( Complex ( 0 , 0 ) / Complex( 0 , 0 ) ) . isFinite)
202
229
XCTAssertFalse ( ( Complex ( 1 , 1 ) / Complex( 0 , 0 ) ) . isFinite)
203
230
XCTAssertFalse ( ( Complex . infinity / Complex( 0 , 0 ) ) . isFinite)
204
231
XCTAssertFalse ( ( Complex . i / Complex( 0 , 0 ) ) . isFinite)
232
+
233
+ }
234
+
235
+ #if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) && LONG_TESTS
236
+ @available ( macOS 11 . 0 , iOS 14 . 0 , tvOS 14 . 0 , watchOS 7 . 0 , * )
237
+ func testFloat16DivisionSemiExhaustive( ) {
238
+ func complex( bitPattern: UInt32 ) -> Complex < Float16 > {
239
+ Complex (
240
+ Float16 ( bitPattern: UInt16 ( truncatingIfNeeded: bitPattern) ) ,
241
+ Float16 ( bitPattern: UInt16 ( truncatingIfNeeded: bitPattern >> 16 ) )
242
+ )
243
+ }
244
+ for bits in 0 ... UInt32 . max {
245
+ let a = complex ( bitPattern: bits)
246
+ if bits & 0xfffff == 0 { print ( a) }
247
+ let b = complex ( bitPattern: UInt32 . random ( in: 0 ... . max) )
248
+ var q = Complex < Float > ( a) / Complex < Float > ( b)
249
+ if checkDivide ( a, b, expected: Complex < Float16 > ( q) , ulps: 4 ) { XCTFail ( ) }
250
+ q = Complex < Float > ( b) / Complex < Float > ( a)
251
+ if checkDivide ( b, a, expected: Complex < Float16 > ( q) , ulps: 4 ) { XCTFail ( ) }
252
+ }
205
253
}
254
+ #endif
206
255
}
0 commit comments