The Crystal Programming Language Forum

Floats : equality compare

The comparison of 2 floating-point numbers with == is not reliable, as everyone knows.
Nevertheless, it is sometimes complicated to do without it.

Wouldn’t it be a good idea to integrate in the stdlib an “approximate” comparison function, such as the one defined here, and even to overload the =~ and !~ operators for this purpose?

Thoughts?

I think this is mostly needed in specs, and there’s a matcher for that already:

https://crystal-lang.org/api/1.0.0/Spec/Expectations.html#be_close(expected,delta)-instance-method

Or were you talking about using it in regular code? What’s the use case?

Yes, I mean for standard code, not for tests, and to do what the == operator exists for, but with increased reliability, and without having to look for workarounds (with < or >) or code logic changes, and in my current development project, the use of floats cannot be avoided.

According to what I read, be_close just makes a simple comparison of the difference of 2 values with a delta, quite far from more elaborate functions like the one referenced in the link of my post or here

For the time being, I will adapt the almost_equal function in Java for the Crystal language for my own use.

Comparison between two floating point values is actually exact and reliable. Imprecise is the conversion between floating point format and decimal representation.

Outside of specs which test algorithms to return specific values expressed as literals, this should rarely be an issue.
Can you tell us about your use case?

Reading the mentioned posts and also this blog, I was wondering about the reliability of the test x == 0.0 when x is the result of a series of calculations on a data history (which is my use case)
If I understand correctly, this test will return a false result in all cases where x is not exactly equal to 0.0, hence my interest in a test of approximate equality in the case where, for example, x would have a value very close to 0.0
But thinking about it, a test like if -epsilon < x < epsilon, with epsilon = 0.0000000001 for example, will do just as well!
Thanks for your comments

It looks like you’ve already figured out a good solution, but I wanted to weigh in with something maybe obvious just because it wasn’t explicitly stated: the problem with using =~ and !~ is that they use two values (the receiver on the left side and the argument on the right), but what you want is something that uses three values (the receiver, the argument, and an epsilon). Your two links and the solution you came to (-epsilon < x < epsilon) all use an epsilon, and that value should be implementation-dependent.

For example, if I’m working on values for some GIS system, I might use an epsilon of 10e-6 for latitude and longitude, but if I want a consistent epsilon when I’m considering values in kilometers I’d want to use 10e-4, since both values come out to around 10 cm. Any standard library implementation of a closeness method would need to take an epsilon, which unfortunately rules out graceful use of operators like ~=.

I suspect you already know this, but I wanted to make sure that future readers have an explicit explanation.

3 Likes