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

Ordering methods are inconsistent for Double and Float #10511

Closed
NthPortal opened this issue Sep 15, 2017 · 24 comments
Closed

Ordering methods are inconsistent for Double and Float #10511

NthPortal opened this issue Sep 15, 2017 · 24 comments
Assignees
Labels
Milestone

Comments

@NthPortal
Copy link

NthPortal commented Sep 15, 2017

For:

  • Float.NaN
  • Double.NaN
  • -0.0f and 0.0f
  • -0.0d and 0.0d

the following inconsistencies exist:

  • Ordering.lt can return false when Ordering.compare returns a negative number
  • Ordering.lteq can return false when Ordering.compare returns a negative number or 0
  • Ordering.equiv can return false when Ordering.compare returns 0
  • Ordering.equiv can return true when Ordering.compare returns a non-zero value
  • Ordering.gteq can return false when Ordering.compare returns a positive number or 0
  • Ordering.gt can return false when Ordering.compare returns a positive number

The reason for the inconsistency is that the convenience methods are overridden to use the primitive operators (<, <=, ==, >=, and > respectively), which always return false for operations where at one of the operands is NaN. However compare is implemented by java.lang.{Float, Double}.compare, for which NaN is ordered as larger than all other values. Also, the primitive operators treat -0.0 and 0.0 as equal, while java.lang.{Float, Double}.compare do not.

Ordering.min is inconsistent with compare as well; however, I'm not sure it is consistent with the other methods either. Ordering.max seems to be consistent with compare by chance.

It is possible that this behavior is intended, though counter-intuitive, but if so, it should probably be documented.


One result of these inconsistencies is that TraversableOnce.min and .max are non-deterministic (and wrong):

scala> import java.lang.Double.NaN
import java.lang.Double.NaN

scala> Seq(5.0, 3.0, NaN, 4.0)
res0: Seq[Double] = List(5.0, 3.0, NaN, 4.0)

scala> res0.min
res1: Double = 4.0

scala> res0.max
res2: Double = 4.0

Those methods could be fixed by using Ordering.min and .max internally, instead of .lteq and .gteq; however, they shouldn't need to.

@NthPortal NthPortal changed the title Ordering.equiv is inconsistent with Ordering.compare for Double and Float Convenience methods for Ordering are inconsistent with Ordering.compare for Double and Float Feb 10, 2018
@NthPortal
Copy link
Author

I've discovered that the problem is worse than I originally thought, so I've updated the issue description accordingly.

@NthPortal
Copy link
Author

NthPortal commented Feb 10, 2018

As I see it, there are three options here:

  1. Leave the behaviour as is, and document it.
  2. Remove the overrides for the other methods.
  3. Change the definitions of the other methods to agree with compare (while still having slightly less overhead than calling compare).

I am in favour of option 2, but I'm happy to make and PR changes based on any of them.

@NthPortal NthPortal changed the title Convenience methods for Ordering are inconsistent with Ordering.compare for Double and Float Ordering methods are inconsistent for Double and Float Feb 10, 2018
@NthPortal
Copy link
Author

Extended testing leaves me convinced that option 3 is extremely difficult, if not impossible, to do correctly. I'm happy for someone to prove me wrong though.

@NthPortal
Copy link
Author

I've dug up the original source of this problem, which seems to be #5104, and this commit. While the commit does make Ordering.min and Ordering.max consistent with math.min and math.max, it makes the Ordering itself internally inconsistent (in multiple ways). I opine that internal consistency is vastly more important, and the lack of it much more confusing when stumbled across.

NthPortal added a commit to NthPortal/scala that referenced this issue Feb 11, 2018
Remove overridden method implementations in Ordering.

Fixes scala/bug#10511
NthPortal added a commit to NthPortal/scala that referenced this issue Feb 11, 2018
Remove overridden method implementations in Ordering.

Reverts 460bbc1

Fixes scala/bug#10511
NthPortal added a commit to NthPortal/scala that referenced this issue Feb 13, 2018
Remove overridden method implementations in Ordering.

Reverts 460bbc1

Fixes scala/bug#10511
@NthPortal
Copy link
Author

See also scala/scala#76

NthPortal added a commit to NthPortal/scala that referenced this issue Feb 28, 2018
Remove overridden method implementations in Ordering.

Reverts 460bbc1

Fixes scala/bug#10511
NthPortal added a commit to NthPortal/scala that referenced this issue Feb 28, 2018
Remove overridden method implementations in Ordering.

Reverts 460bbc1

Fixes scala/bug#10511
@NthPortal
Copy link
Author

I've discovered more problems caused by these inconsistencies regarding min and max on collections, and updated the description accordingly

@odersky

This comment has been minimized.

@NthPortal
Copy link
Author

NthPortal commented Dec 30, 2019

for reference, most of the discussion happened at scala/scala#6323 and scala/scala#6410

(I'm mostly leaving this comment because I keep forgetting where they took place myself)

@NthPortal

This comment has been minimized.

@odersky

This comment has been minimized.

@NthPortal

This comment has been minimized.

@NthPortal

This comment has been minimized.

@NthPortal

This comment has been minimized.

@NthPortal NthPortal reopened this Dec 31, 2019
@NthPortal NthPortal modified the milestones: 2.13.0-M5, 2.13.2 Dec 31, 2019
@NthPortal

This comment has been minimized.

@NthPortal
Copy link
Author

good catch there Seth - this is a nightmarishly bad first issue 😄

@sjrd
Copy link
Member

sjrd commented Dec 31, 2019

IMO a new issue should be created and all messages since #10511 (comment) should be transferred to the new issue. It makes no sense to me to reopen this issue and refix it. The current issue was fixed in 2.13.0-M5 already.

@odersky

This comment has been minimized.

@NthPortal

This comment has been minimized.

@dwijnand

This comment has been minimized.

@odersky

This comment has been minimized.

@odersky

This comment has been minimized.

@dwijnand

This comment has been minimized.

@odersky

This comment has been minimized.

@eed3si9n
Copy link
Member

eed3si9n commented Jan 2, 2020

I opened #11844, so I'm closing this issue here.

@eed3si9n eed3si9n closed this as completed Jan 2, 2020
@dwijnand dwijnand modified the milestones: 2.13.2, 2.13.0-M5 Jan 2, 2020
eed3si9n added a commit to eed3si9n/scala that referenced this issue Feb 15, 2020
Fixes scala/bug#11844
Ref scala/bug#10511
Ref scala#6410
Ref scala#76

This change the deprecation of `DeprecatedDoubleOrdering` to a migration warning instead to avoid `List(1.0, -1.0).sorted` giving deprecation warning.

This also provides some documentation on the ordering instances in Scaladoc.
eed3si9n added a commit to eed3si9n/scala that referenced this issue Feb 18, 2020
Fixes scala/bug#11844
Ref scala/bug#10511
Ref scala#6410
Ref scala#76

This change the deprecation of `DeprecatedDoubleOrdering` to a migration warning instead to avoid `List(1.0, -1.0).sorted` giving deprecation warning.

This also provides some documentation on the ordering instances in Scaladoc.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment