Skip to content

Commit 4125ac2

Browse files
committed
Add Swifty nan() function with static .c namespace
Improves ergonomics of nan() function creation by: - Adding String-based nan() wrapper to Double.C and Float.C - Adding static .c property to Double and Float for type-level access - Enabling idiomatic syntax: Double.c.nan() and Float.c.nan() API Changes: - Double.C.nan(_ tag: String = "") -> Double - Float.C.nan(_ tag: String = "") -> Float - Double.c (static) -> C.Type - Float.c (static) -> C.Type Usage: // Simple NaN let nan1 = Double.c.nan() // NaN with diagnostic payload let nan2 = Double.c.nan("overflow") let nan3 = Float.c.nan("underflow") Authoritative API (ISO_9899.Math.nan) remains unchanged for standards compliance. The Swifty wrapper eliminates need for .withCString closures while maintaining zero-overhead delegation via @_transparent. Tests: Added 8 comprehensive tests in NaNSwiftyAPITests.swift All 183 tests pass
1 parent 496a58c commit 4125ac2

File tree

3 files changed

+172
-0
lines changed

3 files changed

+172
-0
lines changed

Sources/ISO 9899/Double+ISO_9899.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ extension Double {
2020
/// let sine = x.c.sin // 0.90929742...
2121
/// let log = x.c.log // 0.693147180...
2222
/// let abs = (-x).c.abs // 2.0
23+
///
24+
/// // Static methods
25+
/// let nan = Double.c.nan("overflow") // NaN with payload
2326
/// ```
2427
///
2528
/// ## See Also
@@ -29,6 +32,20 @@ extension Double {
2932
C(value: self)
3033
}
3134

35+
/// Static access to C standard library math type-level functions via `.c` namespace
36+
///
37+
/// Provides ergonomic access to type-level C math functions like `nan()`.
38+
///
39+
/// ## Usage
40+
///
41+
/// ```swift
42+
/// let nan = Double.c.nan() // Plain NaN
43+
/// let tagged = Double.c.nan("overflow") // NaN with diagnostic payload
44+
/// ```
45+
public static var c: C.Type {
46+
C.self
47+
}
48+
3249
/// C standard library math operations namespace for Double
3350
///
3451
/// Provides ergonomic instance access to C math functions.
@@ -441,6 +458,31 @@ extension Double.C {
441458
// MARK: - Manipulation Functions (Section 7.12.11)
442459

443460
extension Double.C {
461+
/// Create a quiet NaN with optional diagnostic payload
462+
///
463+
/// Creates a quiet NaN value with an optional string tag that encodes a payload
464+
/// in the NaN's trailing fraction field. This can be useful for debugging or
465+
/// tracking the origin of NaN values.
466+
///
467+
/// The payload interpretation is implementation-defined. Most implementations
468+
/// either ignore the tag or interpret it as an integer.
469+
///
470+
/// Delegates to ``ISO_9899/Math/nan(_:)``
471+
///
472+
/// - Parameter tag: Optional string tag for NaN payload (defaults to empty string)
473+
/// - Returns: A quiet NaN value
474+
///
475+
/// ## Example
476+
/// ```swift
477+
/// let nan1 = Double.c.nan() // Plain NaN
478+
/// let nan2 = Double.c.nan("overflow") // NaN with diagnostic tag
479+
/// let nan3 = Double.c.nan("division-by-zero") // Different payload
480+
/// ```
481+
@_transparent
482+
public static func nan(_ tag: String = "") -> Double {
483+
tag.withCString { ISO_9899.Math.nan($0) }
484+
}
485+
444486
/// Returns self with the sign of another value
445487
///
446488
/// Returns a value with the magnitude of self and the sign of `other`.

Sources/ISO 9899/Float+ISO_9899.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ extension Float {
2020
/// let sine = x.c.sin // 0.90929742...
2121
/// let log = x.c.log // 0.693147180...
2222
/// let abs = (-x).c.abs // 2.0
23+
///
24+
/// // Static methods
25+
/// let nan = Float.c.nan("overflow") // NaN with payload
2326
/// ```
2427
///
2528
/// ## See Also
@@ -29,6 +32,20 @@ extension Float {
2932
C(value: self)
3033
}
3134

35+
/// Static access to C standard library math type-level functions via `.c` namespace
36+
///
37+
/// Provides ergonomic access to type-level C math functions like `nan()`.
38+
///
39+
/// ## Usage
40+
///
41+
/// ```swift
42+
/// let nan = Float.c.nan() // Plain NaN
43+
/// let tagged = Float.c.nan("overflow") // NaN with diagnostic payload
44+
/// ```
45+
public static var c: C.Type {
46+
C.self
47+
}
48+
3249
/// C standard library math operations namespace for Float
3350
///
3451
/// Provides ergonomic instance access to C math functions.
@@ -441,6 +458,31 @@ extension Float.C {
441458
// MARK: - Manipulation Functions (Section 7.12.11)
442459

443460
extension Float.C {
461+
/// Create a quiet NaN with optional diagnostic payload
462+
///
463+
/// Creates a quiet NaN value with an optional string tag that encodes a payload
464+
/// in the NaN's trailing fraction field. This can be useful for debugging or
465+
/// tracking the origin of NaN values.
466+
///
467+
/// The payload interpretation is implementation-defined. Most implementations
468+
/// either ignore the tag or interpret it as an integer.
469+
///
470+
/// Delegates to ``ISO_9899/Math/nanf(_:)``
471+
///
472+
/// - Parameter tag: Optional string tag for NaN payload (defaults to empty string)
473+
/// - Returns: A quiet NaN value
474+
///
475+
/// ## Example
476+
/// ```swift
477+
/// let nan1 = Float.c.nan() // Plain NaN
478+
/// let nan2 = Float.c.nan("overflow") // NaN with diagnostic tag
479+
/// let nan3 = Float.c.nan("division-by-zero") // Different payload
480+
/// ```
481+
@_transparent
482+
public static func nan(_ tag: String = "") -> Float {
483+
tag.withCString { ISO_9899.Math.nanf($0) }
484+
}
485+
444486
/// Returns self with the sign of another value
445487
///
446488
/// Returns a value with the magnitude of self and the sign of `other`.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
@testable import ISO_9899
2+
import Testing
3+
4+
@Suite("NaN Creation - Swifty API")
5+
struct NaNSwiftyAPITests {
6+
7+
@Test("Double.c.nan() creates NaN")
8+
func doubleNanBasic() {
9+
let result = Double.c.nan()
10+
#expect(result.isNaN)
11+
#expect(result.c.isNaN)
12+
}
13+
14+
@Test("Double.c.nan(tag) creates NaN with payload")
15+
func doubleNanWithTag() {
16+
let result = Double.c.nan("overflow")
17+
#expect(result.isNaN)
18+
#expect(result.c.isNaN)
19+
}
20+
21+
@Test("Different tags produce different bit patterns")
22+
func doubleNanDifferentPayloads() {
23+
let nan1 = Double.c.nan("")
24+
let nan2 = Double.c.nan("1")
25+
let nan3 = Double.c.nan("2")
26+
27+
// All should be NaN
28+
#expect(nan1.isNaN)
29+
#expect(nan2.isNaN)
30+
#expect(nan3.isNaN)
31+
32+
// But with different bit patterns (payloads)
33+
// Note: Empty string might produce same as some default
34+
#expect(nan2.bitPattern != nan3.bitPattern)
35+
}
36+
37+
@Test("Float.c.nan() creates NaN")
38+
func floatNanBasic() {
39+
let result = Float.c.nan()
40+
#expect(result.isNaN)
41+
#expect(result.c.isNaN)
42+
}
43+
44+
@Test("Float.c.nan(tag) creates NaN with payload")
45+
func floatNanWithTag() {
46+
let result = Float.c.nan("underflow")
47+
#expect(result.isNaN)
48+
#expect(result.c.isNaN)
49+
}
50+
51+
@Test("NaN with payload has correct classification")
52+
func nanClassification() {
53+
let result = Double.c.nan("test")
54+
#expect(result.c.classification == .nan)
55+
#expect(result.c.isNaN == true)
56+
#expect(result.c.isFinite == false)
57+
#expect(result.c.isInfinite == false)
58+
#expect(result.c.isNormal == false)
59+
}
60+
61+
@Test("NaN comparisons work correctly")
62+
func nanComparisons() {
63+
let nan1 = Double.c.nan("a")
64+
let nan2 = Double.c.nan("b")
65+
66+
// NaN comparisons should return false
67+
#expect(nan1.c.isGreater(than: nan2) == false)
68+
#expect(nan1.c.isLess(than: nan2) == false)
69+
#expect(nan1.c.isNotEqual(to: nan2) == false)
70+
71+
// But they should be unordered
72+
#expect(nan1.c.isUnordered(with: nan2) == true)
73+
#expect(nan1.c.isUnordered(with: 5.0) == true)
74+
}
75+
76+
@Test("Authoritative API still works")
77+
func authoritativeAPICompatibility() {
78+
let swiftyNaN = Double.c.nan("auth")
79+
let authNaN = "auth".withCString { ISO_9899.Math.nan($0) }
80+
81+
// Both should be NaN
82+
#expect(swiftyNaN.isNaN)
83+
#expect(ISO_9899.Math.isnan(authNaN))
84+
85+
// And should have same bit pattern
86+
#expect(swiftyNaN.bitPattern == authNaN.bitPattern)
87+
}
88+
}

0 commit comments

Comments
 (0)