Take a custom IComparer, that treats two doubles as equal if their difference is less than a given epsilon.
What would happen if this IComparer is used in a OrderBy().ThenBy() clause?
Specifically I am thinking of the following implementation:
public class EpsilonComparer : IComparer<double>
{
private readonly double epsilon;
public EpsilonComparer(double epsilon)
{
this.epsilon = epsilon;
}
public int Compare(double d1, double d2)
{
if (Math.Abs(d1-d2)<=epsilon) return 0;
return d1.CompareTo(d2);
}
}
Now this IComparer relationship is clearly not transitive. (if a ~ b and b ~ c then a ~ c)
With epsilon== 0.6 :
- Compare(1, 1.5) == 0
- Compare(1.5, 2) == 0
yet - Compare(1, 2 ) == -1
What would happen if this IComparer was used in an OrderBy query, like this:
List<Item> itemlist;
itemList = itemlist.OrderBy(item=>item.X, new EpsilonComparer(0.352))
.ThenBy (item=>item.Y, new EpsilonComparer(1.743)).ToList();
Would the sort behave as one would expect, sorting the list first by X, then by Y, while treating roughly equal values as exactly equal?
Would it blow up under certain circumstances?
Or is this whole sort ill-defined?
What exactly are the consequences of using an IComparer without transitivity?
(I know that this is most likely undefined behavior of the c# language. I am still very much interested in an answer.)
And is there an alternative way to get this sorting behaviour?
(besides rounding the values, which would introduce artifacts when for two close doubles one is rounded up and the other down)
An online fidle of the code in this question is available here:
view my code snippet here. it's just for first level sorting and not optimized.
OrderBy and ThenBy are using general algorithm. you need to re-implement OrderBy and ThenBy with special algorithm like mine. then it can work as
OrderBy().ThenBy().Detail of algorithm:
in a sorted sequence(x1 x2 x3...) under your EpsilonComparer, if x4>x1, then x5>x1. if x4=x1, then x3=x1 and either x5>x1 or x5=x1.
with epsilon(0.4), input following numbers:0.1, 0.6, 1, 1.1, 1.6, 2, 2, 2.6, 3, 3.1, 3.6, 4, 4.1, 4.6, 5, 5.1, 5.6, 6, 6.1, 6.6
result:0.1 0.6 1 1.1 (1.6 2 2 ) 2.6 3 3.1 3.6 4 4.1 4.6 (5 5.1 ) 5.6 (6 6.1 ) 6.6
(a,b,c) indicates these numbers are equal and the order of numbers is not fixed. they can be (a,b,c), (c,a,b) and any other order.
a b indicates a < b and order is fixed.