Stream: contributing

Topic: floating point equality


view this post on Zulip Brian Teague (Jan 12 2024 at 19:44):

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

view this post on Zulip Brian Teague (Jan 12 2024 at 21:00):

I need to do more research on how, Rust, Haskell, Python handle floating point equality.

view this post on Zulip Richard Feldman (Jan 12 2024 at 21:41):

you can get a == b for floats with a >= b && a <= b - it optimizes to equality behind the scenes

view this post on Zulip Brendan Hansknecht (Jan 12 2024 at 23:47):

isApproxEq should consider 2 inifinities as not equal

view this post on Zulip Brendan Hansknecht (Jan 12 2024 at 23:47):

So it should just need to be Num.absDiff value referenceValue <= absoluteTolerance + relativeTolerance * Num.abs referenceValue

view this post on Zulip Brian Teague (Jan 13 2024 at 15:33):

I think most languages return true when comparing infinity to infinity.

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 17:09):

For equality, yes (but only cause they have to to follow the ieee spec and it is the simple answer).

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 17:09):

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.

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 17:16):

Can we make that an optional flag that defaults to true infinitiesEquivalent ? True

view this post on Zulip Richard Feldman (Jan 13 2024 at 20:06):

are the extra conditionals there worth it for the edge case, given that this is a numeric comparison that might be performed a lot?

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 20:18):

I mean if you expect infinities to be equivalent, it is required.

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 20:19):

This should compile down to something branchless

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 20:19):

So infinitiesEquivalent is just an extra boolean and.

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 20:20):

All the float numbers will outweigh that and there should be a lot of ILP, so I wouldn't really be concerned

view this post on Zulip Brendan Hansknecht (Jan 13 2024 at 20:20):

Though I might explictly write the branchless form in roc just to be safe

view this post on Zulip Brian Teague (Jan 18 2024 at 14:02):

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