22//
33// This source file is part of the Swift Numerics open source project
44//
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
66// Licensed under Apache License v2.0 with Runtime Library Exception
77//
88// See https://swift.org/LICENSE.txt for license information
@@ -13,11 +13,23 @@ import XCTest
1313import ComplexModule
1414import 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
1721func 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
2335func closeEnough< T: Real > ( _ a: T , _ b: T , ulps allowed: T ) -> Bool {
@@ -29,11 +41,15 @@ func checkMultiply<T>(
2941 _ a: Complex < T > , _ b: Complex < T > , expected: Complex < T > , ulps allowed: T
3042) -> Bool {
3143 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 }
3248 let rel = relativeError ( observed, expected)
33- if rel > allowed {
49+ guard rel <= allowed else {
3450 print ( " Over-large error in \( a) * \( b) " )
3551 print ( " Expected: \( expected) \n Observed: \( observed) " )
36- print ( " Relative error was \( rel) (tolerance: \( allowed) . " )
52+ print ( " Relative error was \( rel) (tolerance: \( allowed) ) ." )
3753 return true
3854 }
3955 return false
@@ -43,11 +59,15 @@ func checkDivide<T>(
4359 _ a: Complex < T > , _ b: Complex < T > , expected: Complex < T > , ulps allowed: T
4460) -> Bool {
4561 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 }
4666 let rel = relativeError ( observed, expected)
47- if rel > allowed {
67+ guard rel <= allowed else {
4868 print ( " Over-large error in \( a) / \( b) " )
4969 print ( " Expected: \( expected) \n Observed: \( observed) " )
50- print ( " Relative error was \( rel) (tolerance: \( allowed) . " )
70+ print ( " Relative error was \( rel) (tolerance: \( allowed) ) ." )
5171 return true
5272 }
5373 return false
@@ -63,7 +83,6 @@ final class ArithmeticTests: XCTestCase {
6383 func testPolar< T> ( _ type: T . Type )
6484 where T: BinaryFloatingPoint , T: Real ,
6585 T. Exponent: FixedWidthInteger , T. RawSignificand: FixedWidthInteger {
66-
6786 // In order to support round-tripping from rectangular to polar coordinate
6887 // systems, as a special case phase can be non-finite when length is
6988 // either zero or infinity.
@@ -76,10 +95,9 @@ final class ArithmeticTests: XCTestCase {
7695 XCTAssertEqual ( Complex < T > ( length: - . infinity, phase: . infinity) , . infinity)
7796 XCTAssertEqual ( Complex < T > ( length: - . infinity, phase: - . infinity) , . infinity)
7897 XCTAssertEqual ( Complex < T > ( length: - . infinity, phase: . nan ) , . infinity)
79-
98+
8099 let exponentRange =
81- ( T . leastNormalMagnitude. exponent + T. Exponent ( T . significandBitCount) ) ...
82- T . greatestFiniteMagnitude. exponent
100+ T . leastNormalMagnitude. exponent ... T . greatestFiniteMagnitude. exponent
83101 let inputs = ( 0 ..< 100 ) . map { _ in
84102 Polar ( length: T (
85103 sign: . plus,
@@ -136,20 +154,29 @@ final class ArithmeticTests: XCTestCase {
136154 // Now test multiplication and division using the polar inputs:
137155 for q in inputs {
138156 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
140160 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
142164 if checkDivide ( z, w, expected: quotient, ulps: 16 ) { XCTFail ( ) }
143165 }
144166 }
145167 }
146168
147169 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
148175 testPolar ( Float . self)
149176 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)
151178 testPolar ( Float80 . self)
152- #endif
179+ #endif
153180 }
154181
155182 func testBaudinSmith( ) {
@@ -191,16 +218,38 @@ final class ArithmeticTests: XCTestCase {
191218 Complex ( 1.02951151789360578e-84 , 6.97145987515076231e-220 ) ) ,
192219 ]
193220 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 ( ) }
195222 if checkDivide ( test. a, test. c, expected: test. b, ulps: 1.0 ) { XCTFail ( ) }
196223 if checkMultiply ( test. b, test. c, expected: test. a, ulps: 1.0 ) { XCTFail ( ) }
197224 }
198225 }
199-
226+
200227 func testDivisionByZero( ) {
201228 XCTAssertFalse ( ( Complex ( 0 , 0 ) / Complex( 0 , 0 ) ) . isFinite)
202229 XCTAssertFalse ( ( Complex ( 1 , 1 ) / Complex( 0 , 0 ) ) . isFinite)
203230 XCTAssertFalse ( ( Complex . infinity / Complex( 0 , 0 ) ) . isFinite)
204231 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+ }
205253 }
254+ #endif
206255}
0 commit comments