Float/double equality comparers
ljani opened this issue · 4 comments
How about adding special equality comparers for float
and double
types, which allow you to specify the allowed tolerance for difference?
As far as I know there are none in .NET or this library.
I like it! The next update is adding string-specific comparers, so this will go well as a part of that update.
Looks like it won't be very straightforward:
https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
https://www.boost.org/doc/libs/1_72_0/libs/test/doc/html/boost_test/testing_tools/extended_comparison/floating_point/floating_points_comparison_impl.html
In particular, "tolerance" equality isn't transitive, so I'm not sure if these can be made into equality operators or not.
Yeah, it might not make sense to add it to the core library.
I was alternatively thinking having an overload accepting a lambda (Func<T, T, bool>
) for eg. ThenEquateBy
to test the difference. Similarly, you'd need to throw NotSupportedException
from GetHashCode
.
My use case is mainly testing. I usually add my own IEqualityComparer<float>
for each project I have, but it'd be nice to have it in a library with other similar functionality.
Here's an example what I'd like to do:
var comparer = EqualityComparerBuilder
.For<DataSample>()
.EquateBy(sample => sample.Timestamp)
.ThenEquateBy(sample => sample.Value, (a, b) => Math.Abs(a - b) < 0.1f);
Assert.Equal(expected, actual, comparer);
EDIT: DataSample.Value
is a float
.
The problem with doing that to ThenEquateBy
is that it does return an IEqualityComparer<T>
which has unexpected behavior (not transitive, throwing for GetHashCode
). Major version 4 of this library did have "anonymous comparers" which allowed passing delegates as you suggest, but this was removed since it is too easy to create comparers that don't work correctly (e.g., a tolerance equality comparer).
I think it does make sense for this library to implement a kind of "best known practice" for floating-point comparisons (tolerance and relative tolerance) but they would be in their own utility class and not ever exposed as actual comparers (and by extension, not be used in ThenEquateBy
, etc). For your use case, I'd recommend writing an AssertEx.Equal
that used those utility methods, and if code wanted to do floating-point comparisons at runtime, it could invoke the utility methods directly.