Trying to implement isApproxEq and ran into this compile error:
Note: I can't derive Bool.isEq for floating-point types. That's
because Roc's floating-point numbers cannot be compared for total
equality - in Roc, NaN
is never comparable to NaN
. If a type
doesn't support total equality, it cannot support the Eq ability!
I need value == referenceValue for positive, negative infinity, +0 = -0, and exact matches.
If value or reference is Nan, it should always return false.
isApproxEq : { value: Frac a, referenceValue: Frac a, relativeTolerance ? Frac a, absoluteTolerance ? Frac a } -> Bool
isApproxEq = \{ value, referenceValue, relativeTolerance? 0.00001, absoluteTolerance? 0.000000001 }
->
if value == referenceValue then # For infinity
Bool.true
else
Num.absDiff value referenceValue <= absoluteTolerance + relativeTolerance * Num.abs referenceValue
I need to do more research on how, Rust, Haskell, Python handle floating point equality.
you can get a == b
for floats with a >= b && a <= b
- it optimizes to equality behind the scenes
isApproxEq should consider 2 inifinities as not equal
So it should just need to be Num.absDiff value referenceValue <= absoluteTolerance + relativeTolerance * Num.abs referenceValue
I think most languages return true when comparing infinity to infinity.
For equality, yes (but only cause they have to to follow the ieee spec and it is the simple answer).
For approximately equality that is actually useful, generally no to my understanding
Seems more things than expect default to them being equal. Actually a bit surprised. That will hide bugs in certain places.
Can we make that an optional flag that defaults to true infinitiesEquivalent ? True
are the extra conditionals there worth it for the edge case, given that this is a numeric comparison that might be performed a lot?
I mean if you expect infinities to be equivalent, it is required.
This should compile down to something branchless
So infinitiesEquivalent
is just an extra boolean and.
All the float numbers will outweigh that and there should be a lot of ILP, so I wouldn't really be concerned
Though I might explictly write the branchless form in roc just to be safe
I modified the tests to match numpy's website. The tests are passing including Infinity and NaN.
The PR is ready for review.
Last updated: Jul 06 2025 at 12:14 UTC