Skip to content

docs: add dedicated documentation for ApproximateEquality - #355

Merged
stephentyrone merged 2 commits into
apple:mainfrom
reginaldalfret:docs/approximate-equality
Oct 8, 2026
Merged

stephentyrone merged 2 commits into
apple:mainfrom
reginaldalfret:docs/approximate-equality

Conversation

@reginaldalfret

@reginaldalfret reginaldalfret commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR addresses #153 by adding a dedicated DocC documentation article for ApproximateEquality, providing comprehensive background, examples, and technical guidance for comparing numerical values up to a tolerance.

Specifically:

  • Sources/RealModule/Documentation.docc/ApproximateEquality.md:
    • Explains why == is usually inappropriate for floating-point values and introduces isApproximatelyEqual.
    • Details relative tolerance (.ulpOfOne.squareRoot(), scale calculation) and absolute tolerance (especially near zero), along with code snippets.
    • Documents special value handling (NaN, infinities, +0.0 vs -0.0).
    • Explains custom norms on AdditiveArithmetic (e.g. Euclidean length \.length on ComplexModule).
    • Discusses mathematical properties (reflexive, symmetric, non-transitive / convex) and explicitly documents why approximate equality must not be used to implement Equatable.
  • Sources/RealModule/Documentation.docc/RealModule.md:
    • Adds a reference paragraph pointing to <doc:ApproximateEquality>.
  • Sources/RealModule/README.md:

Resolves #153.

Testing

Documentation-only changes; verified markdown formatting, links, and doc references.

Add a comprehensive DocC guide for ApproximateEquality covering:
- Background and motivation for approximate equality in floating-point math
- Relative and absolute tolerance guidelines and formulas
- Special value handling (NaN, infinities, signed zeroes)
- Custom norms with AdditiveArithmetic
- Mathematical properties (non-transitivity and why not to use for Equatable)

Also update RealModule.md to link to the DocC article and restore the
Approximate equality section with a link in Sources/RealModule/README.md.

Resolves apple#153.

Signed-off-by: reginaldalfret <reginaldalfret@gmail.com>
@@ -0,0 +1,155 @@
# Approximate Equality

Compare floating-point and numerical values up to a tolerance.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The abstract for this article is tricky, because these operations are very general. Formally they don't only work for floating-point or even "numerical" values, though that's the most obvious use case. Mathematically it's roughly something like "compare elements of a normed linear space with tolerance", but that's obviously not a great fit for this context, since we'd have to spend a few dozen paragraphs defining each of those words. I'll think about this a bit, but if you have other suggestions in the meantime, I'd love to hear them.

Floating-point arithmetic is not exact, so two values that are mathematically
equal may differ slightly once they have been computed. Comparing such values
with `==` is usually a mistake: the test will fail for inputs that are equal
in every way that matters to your program.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paragraph is well-intentioned, but misleading. It is frequently completely appropriate to compare floating-point values with exact equality, and frequently desirable to compare non-floating-point values with an approximate equality. Rounding error is one reason why approximate equality can be desirable, but it is not the only one (and not even the primary reason--most nontrivial numerical algorithms have inherent error separate from floating-point considerations, and most measurements are inexact to begin with).

I would just remove this, I think.

let x = 0.1 + 0.2
x == 0.3 // false
x.isApproximatelyEqual(to: 0.3) // true
```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great. If you're open to it, a worked example with a non-Numeric type would also be fantastic here.

result.isApproximatelyEqual(
to: expected,
absoluteTolerance: error,
relativeTolerance: error

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having relativeTolerance == absoluteTolerance only really makes sense in cases where values are expected to lie on an integral grid due to algorithmic considerations. It can certainly happen, but isn't the norm. We should either explain that or talk a little about how to chose values for these appropriately.


For a complex value, the default norm `\.magnitude` measures the difference
inside a square region. To test instead whether a value lies inside a circle of
radius `0.001` centered at `1 + 0i`, pass the Euclidean length as the norm:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should mention here that the advantage of using \.magnitude for complex values is that it is finite and non-zero for any finite non-zero value. It never overflows or underflows, so can be used even with very poorly scaled values. That's not true for \.length or the one-norm, or any other common choice.

Comment thread Sources/RealModule/README.md Outdated

Because floating-point arithmetic is inexact, two values that are
mathematically equal often differ slightly once computed, so comparing them
with `==` is usually a mistake.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strike this sentence.

…raction and tolerances

Signed-off-by: reginaldalfret <reginaldalfret@gmail.com>
scenarios, exact equality (`==`) may be too strict or inappropriate, and an
approximate equality comparison is needed instead. Conversely, exact equality
remains entirely appropriate when checking for identical values, sentinel states,
or integers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really nice improvement, thanks!

@stephentyrone
stephentyrone merged commit 9f745b7 into apple:main Oct 8, 2026
35 checks passed
stephentyrone added a commit that referenced this pull request Oct 8, 2026
docs: add dedicated documentation for ApproximateEquality
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants