Skip to content

Commit 3fa18ae

Browse files
committed
Test improvements
Improved how tolerance is applied close to infinity for complex division (this still could use a more principled approach, but at least we're in the right ballpark now). Tightened up the tolerance for semi-exhaustive testing too, since that can be done now.
1 parent 1ba2178 commit 3fa18ae

File tree

2 files changed

+21
-10
lines changed

2 files changed

+21
-10
lines changed

Sources/ComplexModule/Complex+AlgebraicField.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ extension Complex: AlgebraicField {
4242
return z * w.conjugate.divided(by: lenSq)
4343
}
4444

45+
@_alwaysEmitIntoClient // specialization
46+
@inline(never)
4547
public static func rescaledDivide(_ z: Complex, _ w: Complex) -> Complex {
4648
if w.isZero { return .infinity }
4749
if !w.isFinite { return .zero }

Tests/ComplexTests/ArithmeticTests.swift

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,23 @@ import XCTest
1313
import ComplexModule
1414
import RealModule
1515

16+
func ulpsFromInfinity<T: Real>(_ a: T) -> T {
17+
(.greatestFiniteMagnitude - a) / .greatestFiniteMagnitude.ulp + 1
18+
}
19+
1620
// TODO: improve this to be a general-purpose complex comparison with tolerance
1721
func relativeError<T>(_ a: Complex<T>, _ b: Complex<T>) -> T {
1822
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+
}
2133
}
2234

2335
func closeEnough<T: Real>(_ a: T, _ b: T, ulps allowed: T) -> Bool {
@@ -32,7 +44,7 @@ func checkMultiply<T>(
3244
let rel = relativeError(observed, expected)
3345
// Even if the expected result is finite, we allow overflow if
3446
// the two-norm of the expected result overflows.
35-
if !expected.length.isFinite && !observed.isFinite { return false }
47+
if !observed.isFinite && !expected.length.isFinite { return false }
3648
guard rel <= allowed else {
3749
print("Over-large error in \(a)*\(b)")
3850
print("Expected: \(expected)\nObserved: \(observed)")
@@ -49,7 +61,7 @@ func checkDivide<T>(
4961
let rel = relativeError(observed, expected)
5062
// Even if the expected result is finite, we allow overflow if
5163
// the two-norm of the expected result overflows.
52-
if !expected.length.isFinite && !observed.isFinite { return false }
64+
if !observed.isFinite && !expected.length.isFinite { return false }
5365
guard rel <= allowed else {
5466
print("Over-large error in \(a)/\(b)")
5567
print("Expected: \(expected)\nObserved: \(observed)")
@@ -217,9 +229,7 @@ final class ArithmeticTests: XCTestCase {
217229

218230
}
219231

220-
#if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64))
221-
222-
/*
232+
#if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) && LONG_TESTS
223233
@available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)
224234
func testFloat16DivisionSemiExhaustive() {
225235
func complex(bitPattern: UInt32) -> Complex<Float16> {
@@ -233,11 +243,10 @@ final class ArithmeticTests: XCTestCase {
233243
if bits & 0xfffff == 0 { print(a) }
234244
let b = complex(bitPattern: UInt32.random(in: 0 ... .max))
235245
var q = Complex<Float>(a)/Complex<Float>(b)
236-
if checkDivide(a, b, expected: Complex<Float16>(q), ulps: 32) { XCTFail() }
246+
if checkDivide(a, b, expected: Complex<Float16>(q), ulps: 4) { XCTFail() }
237247
q = Complex<Float>(b)/Complex<Float>(a)
238-
if checkDivide(b, a, expected: Complex<Float16>(q), ulps: 32) { XCTFail() }
248+
if checkDivide(b, a, expected: Complex<Float16>(q), ulps: 4) { XCTFail() }
239249
}
240250
}
241-
*/
242251
#endif
243252
}

0 commit comments

Comments
 (0)