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

SplayTreeSet treats -0 and 0 as different numbers #675

Open
xommmax opened this issue Jun 1, 2024 · 2 comments
Open

SplayTreeSet treats -0 and 0 as different numbers #675

xommmax opened this issue Jun 1, 2024 · 2 comments

Comments

@xommmax
Copy link

xommmax commented Jun 1, 2024

  final mSet = Set<double>();
  mSet.addAll([-0.0, 0.0]);
  print(mSet);

outputs {0}

Meanwhile:

  final mSet = SplayTreeSet<double>();
  mSet.addAll([-0.0, 0.0]);
  print(mSet);

outputs {-0.0, 0}

I don't know if it's expected, but

print(-0.0 == 0.0);

gives me true, so I expect Set (and any of its implementations) to treat it as the same number

@gnprice
Copy link

gnprice commented Jun 11, 2024

This is expected based on the documented semantics.

SplayTreeSet says:

Elements of the set are compared using the compare function passed in the constructor, both for ordering and for equality. If the set contains only an object a, then set.contains(b) will return true if and only if compare(a, b) == 0, and the value of a == b is not even checked. If the compare function is omitted, the objects are assumed to be Comparable, and are compared using their Comparable.compareTo method.

So it's based on compareTo, not ==. And on num.compareTo, we have:

For doubles, the compareTo operation is different from the partial ordering given by operator==, operator< and operator>. For example, IEEE doubles impose that 0.0 == -0.0
This function imposes a complete ordering for doubles. When using compareTo, the following properties hold: …

  • -0.0 is less than 0.0 (and the integer 0), but greater than any non-zero negative value.

and even a pair of examples:

// The following comparisons yield different results than the
// corresponding comparison operators.
print((-0.0).compareTo(0.0));  // => -1
// …

// -0.0, and NaN comparison operators have rules imposed by the IEEE
// standard.
print(-0.0 == 0.0); // => true

That still leaves the question of whether it should behave that way. It's definitely a bit messy, but I'm not sure there's a better alternative. There are good reasons for each of the different pieces of this, even though the combined result feels a bit out of alignment.

In particular, the difference between SplayTreeSet and the default Set implementation is:

  • The default Set is a hash map, so it naturally uses hashCode and == and doesn't look at ordering between elements.
  • SplayTreeSet is a splay tree, which is an ordering-based data structure. So it naturally uses compareTo, and doesn't look at ==.

The two implementations' behavior is perfectly coherent as long as == and compareTo are coherent (and as long as hashCode is itself coherent with ==). When those come apart, they do too. And they come apart when it comes to doubles, because doubles follow IEEE 754 and there are some odd edge cases in IEEE 754.

@xommmax
Copy link
Author

xommmax commented Jul 7, 2024

Thanks for such a great explanation!

@mosuem mosuem transferred this issue from dart-archive/collection Oct 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants