Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Float/double equality comparers #28

Open
ljani opened this issue Mar 4, 2020 · 4 comments
Open

Float/double equality comparers #28

ljani opened this issue Mar 4, 2020 · 4 comments
Assignees

Comments

@ljani
Copy link

ljani commented Mar 4, 2020

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.

@StephenCleary
Copy link
Owner

I like it! The next update is adding string-specific comparers, so this will go well as a part of that update.

@StephenCleary StephenCleary self-assigned this Mar 4, 2020
@StephenCleary
Copy link
Owner

StephenCleary commented Mar 4, 2020

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.

@ljani
Copy link
Author

ljani commented Mar 5, 2020

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.

@StephenCleary
Copy link
Owner

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants