diff --git a/README.md b/README.md index 1653e61..49fbce0 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,7 @@ let current = bestBid.load(ordering: .acquiring) let nan = FixedPointDecimal.nan nan.isNaN // true nan == nan // true (sentinel semantics) -(nan + someValue).isNaN // true (propagates) +nan + someValue // traps (NaN is signalling) nan.description // "nan" ``` @@ -301,7 +301,7 @@ bash Fuzz/run.sh debug run The fuzzer validates invariants across all operations: -- **Arithmetic**: commutativity, NaN propagation, no silent NaN sentinel creation +- **Arithmetic**: commutativity, NaN trapping, no silent NaN sentinel creation - **Comparisons**: strict total order (exactly one of `<`, `==`, `>`) - **Conversions**: String, Double, Decimal, Codable round-trips - **Rounding**: scale-8 identity, no overflow diff --git a/Sources/FixedPointDecimal/FixedPointDecimal.docc/GettingStarted.md b/Sources/FixedPointDecimal/FixedPointDecimal.docc/GettingStarted.md index 69570f0..32cd4a9 100644 --- a/Sources/FixedPointDecimal/FixedPointDecimal.docc/GettingStarted.md +++ b/Sources/FixedPointDecimal/FixedPointDecimal.docc/GettingStarted.md @@ -97,13 +97,13 @@ let i = Int(somePrice) // 123 ## NaN Support -A sentinel-based NaN value propagates through all operations: +NaN is signalling — any arithmetic operation on a NaN value traps immediately: ```swift let missing = FixedPointDecimal.nan missing.isNaN // true -(missing + someValue).isNaN // true (propagates) missing == .nan // true (sentinel semantics) +missing + someValue // traps (precondition failure) ``` ## Codable diff --git a/Sources/FixedPointDecimal/FixedPointDecimal.docc/TypeDesign.md b/Sources/FixedPointDecimal/FixedPointDecimal.docc/TypeDesign.md index 0050467..65935af 100644 --- a/Sources/FixedPointDecimal/FixedPointDecimal.docc/TypeDesign.md +++ b/Sources/FixedPointDecimal/FixedPointDecimal.docc/TypeDesign.md @@ -31,7 +31,7 @@ This value was chosen because: NaN uses **sentinel semantics**, not IEEE 754: - `NaN == NaN` returns `true` (required for `Hashable` and `Comparable` correctness) - NaN compares less than all non-NaN values (provides a strict total order for sorting) -- Arithmetic with NaN propagates NaN (any operation involving NaN returns NaN) +- Arithmetic with NaN traps (NaN is signalling — any operation involving NaN is a precondition failure) - `init(_ decimal: Decimal)` maps `Decimal.nan` to `.nan`; `Decimal(.nan)` returns `Decimal.nan` - `isFinite` returns `false` for NaN, `true` for all other values - `sign` returns `.plus` for NaN (NaN is excluded from the negative check) diff --git a/Sources/FixedPointDecimal/FixedPointDecimal.swift b/Sources/FixedPointDecimal/FixedPointDecimal.swift index 21db149..3acf2cf 100644 --- a/Sources/FixedPointDecimal/FixedPointDecimal.swift +++ b/Sources/FixedPointDecimal/FixedPointDecimal.swift @@ -393,7 +393,7 @@ public struct FixedPointDecimal: Sendable, BitwiseCopyable { /// Follows the swift-numerics `ElementaryFunctions` naming convention. /// /// Negative exponents compute the reciprocal: `pow(x, -n) == 1 / pow(x, n)`. - /// Returns `.nan` if the base is NaN, or if the result overflows Int64 storage. + /// - Precondition: The base must not be NaN. Traps on overflow. /// /// ```swift /// FixedPointDecimal.pow(10, 3) // 1000