From d86286fc8bf0f7fddda6718c37a16a11297654c9 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Mon, 11 Aug 2025 18:09:22 -0400 Subject: [PATCH] Remove precondition assert from Augmented.sum(large:small:) This is potentially useful to identify some bugs, but there are also algorithms (notably Kahan summation) that can be written in terms of fast2sum but would fail this check. Rework documentation to account for the change. --- Sources/RealModule/AugmentedArithmetic.swift | 40 +++++++++----------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/Sources/RealModule/AugmentedArithmetic.swift b/Sources/RealModule/AugmentedArithmetic.swift index 2e953673..5d3c8e6c 100644 --- a/Sources/RealModule/AugmentedArithmetic.swift +++ b/Sources/RealModule/AugmentedArithmetic.swift @@ -56,6 +56,10 @@ extension Augmented { /// The sum `a + b` represented as an implicit sum `head + tail`. /// + /// - Parameters: + /// - a: The summand with larger magnitude. + /// - b: The summand with smaller magnitude. + /// /// `head` is the correctly rounded value of `a + b`. `tail` is the /// error from that computation rounded to the closest representable /// value. @@ -65,15 +69,13 @@ extension Augmented { /// /// This operation is sometimes called ["fastTwoSum"]. /// - /// - Parameters: - /// - a: The summand with larger magnitude. - /// - b: The summand with smaller magnitude. - /// - /// Preconditions: - /// - /// - `large.magnitude` must not be smaller than `small.magnitude`. - /// They may be equal, or one or both may be `NaN`. - /// This precondition is only enforced in debug builds. + /// > Note: + /// > `tail` is guaranteed to be the best approximation to the error of + /// the sum only if `large.magnitude` >= `small.magnitude`. If this is + /// not the case, then `head` is the correctly rounded sum, but `tail` + /// is not guaranteed to be the exact error. If you do not know a priori + /// how the magnitudes of `a` and `b` compare, you likely want to use + /// ``sum(_:_:)`` instead. /// /// Edge Cases: /// @@ -89,29 +91,23 @@ extension Augmented { /// ["fastTwoSum"]: https://en.wikipedia.org/wiki/2Sum @_transparent public static func sum(large a: T, small b: T) -> (head: T, tail: T) { - assert(!(b.magnitude > a.magnitude)) let head = a + b let tail = a - head + b return (head, tail) } - + /// The sum `a + b` represented as an implicit sum `head + tail`. /// /// `head` is the correctly rounded value of `a + b`. `tail` is the /// error from that computation rounded to the closest representable /// value. /// - /// Unlike `Augmented.sum(large: a, small: b)`, the magnitude of the summands - /// does not matter and `a.magnitude` might as well be strictly less than - /// `b.magnitude`. However, it is recommended to only use this function over - /// `Augmented.sum(large: a, small: b)` in cases where the ordering of the - /// summands magnitude is unknown at compile time. In cases where either of - /// the summands magnitude is guaranteed to be greater than or equal the - /// magnitude of the other summand, use `Augmented.sum(large: a, small: b)` - /// over this function; as it faster to calculate. + /// Unlike ``sum(large:small:)``, the magnitude of the summands does not + /// matter. If you know statically that `a.magnitude >= b.magnitude`, you + /// should use ``sum(large:small:)``. If you do not have such a static + /// bound, you should use this function instead. /// - /// Unlike `Augmented.product(a, b)`, the rounding error of a sum can - /// never underflow. + /// Unlike ``product(_:_:)``, the rounding error of a sum never underflows. /// /// This operation is sometimes called ["twoSum"]. /// @@ -126,7 +122,7 @@ extension Augmented { /// interpreted as having any meaning (it may be `NaN` or `infinity`). /// /// Postconditions: - /// + /// /// - If `head` is normal, then `abs(tail) < head.ulp`. /// Assuming IEEE 754 default rounding, `abs(tail) <= head.ulp/2`. ///