Skip to content

Commit 4bda33d

Browse files
committed
Un-deprecate default floating point Orderings, and change to migration
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.
1 parent 516472d commit 4bda33d

File tree

4 files changed

+112
-25
lines changed

4 files changed

+112
-25
lines changed

src/library/scala/math/Ordering.scala

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package math
1616
import java.util.Comparator
1717

1818
import scala.language.implicitConversions
19+
import scala.annotation.migration
1920

2021
/** Ordering is a trait whose instances each represent a strategy for sorting
2122
* instances of a type.
@@ -369,7 +370,37 @@ object Ordering extends LowPriorityOrderingImplicits {
369370

370371
/** `Ordering`s for `Float`s.
371372
*
372-
* @define floatOrdering Because the behaviour of `Float`s specified by IEEE is
373+
* The behavior of the comparison operations provided by the default (implicit)
374+
* ordering on `Float` changed in 2.10.0 and 2.13.0.
375+
* Prior to Scala 2.10.0, the `Ordering` instance used semantics
376+
* consistent with `java.lang.Float.compare`.
377+
*
378+
* Scala 2.10.0 changed the implementation of `lt`, `equiv`, `min`, etc., to be
379+
* IEEE 754 compliant, while keeping the `compare` method NOT compliant,
380+
* creating an internally inconsistent instance. IEEE 754 specifies that
381+
* `0.0F == -0.0F`. In addition, it requires all comparisons with `Float.NaN` return
382+
* `false` thus `0.0F < Float.NaN`, `0.0F > Float.NaN`, and
383+
* `Float.NaN == Float.NaN` all yield `false`, analogous `None` in `flatMap`.
384+
*
385+
* Recognizing the limitation of the IEEE 754 semantics in terms of ordering,
386+
* Scala 2.13.0 created two instances: `Ordering.Float.IeeeOrdering`, which retains
387+
* the IEEE 754 semantics from Scala 2.12.x, and `Ordering.Float.TotalOrdering`,
388+
* which brings back the `java.lang.Float.compare` semantics for all operations.
389+
* The default extends `TotalOrdering`.
390+
*
391+
* {{{
392+
* List(0.0F, 1.0F, 0.0F / 0.0F, -1.0F / 0.0F).sorted // List(-Infinity, 0.0, 1.0, NaN)
393+
* List(0.0F, 1.0F, 0.0F / 0.0F, -1.0F / 0.0F).min // -Infinity
394+
* implicitly[Ordering[Float]].lt(0.0F, 0.0F / 0.0F) // true
395+
* {
396+
* import Ordering.Float.IeeeOrdering
397+
* List(0.0F, 1.0F, 0.0F / 0.0F, -1.0F / 0.0F).sorted // List(-Infinity, 0.0, 1.0, NaN)
398+
* List(0.0F, 1.0F, 0.0F / 0.0F, -1.0F / 0.0F).min // NaN
399+
* implicitly[Ordering[Float]].lt(0.0F, 0.0F / 0.0F) // false
400+
* }
401+
* }}}
402+
*
403+
* @define floatOrdering Because the behavior of `Float`s specified by IEEE is
373404
* not consistent with a total ordering when dealing with
374405
* `NaN`, there are two orderings defined for `Float`:
375406
* `TotalOrdering`, which is consistent with a total
@@ -380,7 +411,7 @@ object Ordering extends LowPriorityOrderingImplicits {
380411
object Float {
381412
/** An ordering for `Float`s which is a fully consistent total ordering,
382413
* and treats `NaN` as larger than all other `Float` values; it behaves
383-
* the same as [[java.lang.Float#compare]].
414+
* the same as [[java.lang.Float.compare]].
384415
*
385416
* $floatOrdering
386417
*
@@ -401,7 +432,7 @@ object Ordering extends LowPriorityOrderingImplicits {
401432
* `NaN`.
402433
* - `min` and `max` are consistent with `math.min` and `math.max`, and
403434
* return `NaN` when called with `NaN` as either argument.
404-
* - `compare` behaves the same as [[java.lang.Float#compare]].
435+
* - `compare` behaves the same as [[java.lang.Float.compare]].
405436
*
406437
* $floatOrdering
407438
*
@@ -422,14 +453,48 @@ object Ordering extends LowPriorityOrderingImplicits {
422453
}
423454
implicit object IeeeOrdering extends IeeeOrdering
424455
}
425-
@deprecated("There are multiple ways to order Floats (Ordering.Float.TotalOrdering, " +
426-
"Ordering.Float.IeeeOrdering). Specify one by using a local import, assigning an implicit val, or passing it " +
427-
"explicitly. See their documentation for details.", since = "2.13.0")
456+
@migration(
457+
" The default implicit ordering for floats now maintains consistency\n" +
458+
" between its `compare` method and its `lt`, `min`, `equiv`, etc., methods,\n" +
459+
" which means nonconforming to IEEE 754's behavior for -0.0F and NaN.\n" +
460+
" The sort order of floats remains the same, however, with NaN at the end.\n" +
461+
" Import Ordering.Float.IeeeOrdering to recover the previous behavior.\n" +
462+
" See also https://www.scala-lang.org/api/current/scala/math/Ordering$$Float$.html.", "2.13.0")
428463
implicit object DeprecatedFloatOrdering extends Float.TotalOrdering
429464

430465
/** `Ordering`s for `Double`s.
431466
*
432-
* @define doubleOrdering Because the behaviour of `Double`s specified by IEEE is
467+
* The behavior of the comparison operations provided by the default (implicit)
468+
* ordering on `Double` changed in 2.10.0 and 2.13.0.
469+
* Prior to Scala 2.10.0, the `Ordering` instance used semantics
470+
* consistent with `java.lang.Double.compare`.
471+
*
472+
* Scala 2.10.0 changed the implementation of `lt`, `equiv`, `min`, etc., to be
473+
* IEEE 754 compliant, while keeping the `compare` method NOT compliant,
474+
* creating an internally inconsistent instance. IEEE 754 specifies that
475+
* `0.0 == -0.0`. In addition, it requires all comparisons with `Double.NaN` return
476+
* `false` thus `0.0 < Double.NaN`, `0.0 > Double.NaN`, and
477+
* `Double.NaN == Double.NaN` all yield `false`, analogous `None` in `flatMap`.
478+
*
479+
* Recognizing the limitation of the IEEE 754 semantics in terms of ordering,
480+
* Scala 2.13.0 created two instances: `Ordering.Double.IeeeOrdering`, which retains
481+
* the IEEE 754 semantics from Scala 2.12.x, and `Ordering.Double.TotalOrdering`,
482+
* which brings back the `java.lang.Double.compare` semantics for all operations.
483+
* The default extends `TotalOrdering`.
484+
*
485+
* {{{
486+
* List(0.0, 1.0, 0.0 / 0.0, -1.0 / 0.0).sorted // List(-Infinity, 0.0, 1.0, NaN)
487+
* List(0.0, 1.0, 0.0 / 0.0, -1.0 / 0.0).min // -Infinity
488+
* implicitly[Ordering[Double]].lt(0.0, 0.0 / 0.0) // true
489+
* {
490+
* import Ordering.Double.IeeeOrdering
491+
* List(0.0, 1.0, 0.0 / 0.0, -1.0 / 0.0).sorted // List(-Infinity, 0.0, 1.0, NaN)
492+
* List(0.0, 1.0, 0.0 / 0.0, -1.0 / 0.0).min // NaN
493+
* implicitly[Ordering[Double]].lt(0.0, 0.0 / 0.0) // false
494+
* }
495+
* }}}
496+
*
497+
* @define doubleOrdering Because the behavior of `Double`s specified by IEEE is
433498
* not consistent with a total ordering when dealing with
434499
* `NaN`, there are two orderings defined for `Double`:
435500
* `TotalOrdering`, which is consistent with a total
@@ -440,7 +505,7 @@ object Ordering extends LowPriorityOrderingImplicits {
440505
object Double {
441506
/** An ordering for `Double`s which is a fully consistent total ordering,
442507
* and treats `NaN` as larger than all other `Double` values; it behaves
443-
* the same as [[java.lang.Double#compare]].
508+
* the same as [[java.lang.Double.compare]].
444509
*
445510
* $doubleOrdering
446511
*
@@ -461,7 +526,7 @@ object Ordering extends LowPriorityOrderingImplicits {
461526
* `NaN`.
462527
* - `min` and `max` are consistent with `math.min` and `math.max`, and
463528
* return `NaN` when called with `NaN` as either argument.
464-
* - `compare` behaves the same as [[java.lang.Double#compare]].
529+
* - `compare` behaves the same as [[java.lang.Double.compare]].
465530
*
466531
* $doubleOrdering
467532
*
@@ -482,9 +547,13 @@ object Ordering extends LowPriorityOrderingImplicits {
482547
}
483548
implicit object IeeeOrdering extends IeeeOrdering
484549
}
485-
@deprecated("There are multiple ways to order Doubles (Ordering.Double.TotalOrdering, " +
486-
"Ordering.Double.IeeeOrdering). Specify one by using a local import, assigning an implicit val, or passing it " +
487-
"explicitly. See their documentation for details.", since = "2.13.0")
550+
@migration(
551+
" The default implicit ordering for doubles now maintains consistency\n" +
552+
" between its `compare` method and its `lt`, `min`, `equiv`, etc., methods,\n" +
553+
" which means nonconforming to IEEE 754's behavior for -0.0 and NaN.\n" +
554+
" The sort order of doubles remains the same, however, with NaN at the end.\n" +
555+
" Import Ordering.Double.IeeeOrdering to recover the previous behavior.\n" +
556+
" See also https://www.scala-lang.org/api/current/scala/math/Ordering$$Double$.html.", "2.13.0")
488557
implicit object DeprecatedDoubleOrdering extends Double.TotalOrdering
489558

490559
trait BigIntOrdering extends Ordering[BigInt] {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
ordering-migration.scala:3: warning: object DeprecatedFloatOrdering in object Ordering has changed semantics in version 2.13.0:
2+
The default implicit ordering for floats now maintains consistency
3+
between its `compare` method and its `lt`, `min`, `equiv`, etc., methods,
4+
which means nonconforming to IEEE 754's behavior for -0.0F and NaN.
5+
The sort order of floats remains the same, however, with NaN at the end.
6+
Import Ordering.Float.IeeeOrdering to recover the previous behavior.
7+
See also https://www.scala-lang.org/api/current/scala/math/Ordering$$Float$.html.
8+
val f = Ordering[Float]
9+
^
10+
ordering-migration.scala:4: warning: object DeprecatedDoubleOrdering in object Ordering has changed semantics in version 2.13.0:
11+
The default implicit ordering for doubles now maintains consistency
12+
between its `compare` method and its `lt`, `min`, `equiv`, etc., methods,
13+
which means nonconforming to IEEE 754's behavior for -0.0 and NaN.
14+
The sort order of doubles remains the same, however, with NaN at the end.
15+
Import Ordering.Double.IeeeOrdering to recover the previous behavior.
16+
See also https://www.scala-lang.org/api/current/scala/math/Ordering$$Double$.html.
17+
val d = Ordering[Double]
18+
^
19+
ordering-migration.scala:7: warning: object DeprecatedDoubleOrdering in object Ordering has changed semantics in version 2.13.0:
20+
The default implicit ordering for doubles now maintains consistency
21+
between its `compare` method and its `lt`, `min`, `equiv`, etc., methods,
22+
which means nonconforming to IEEE 754's behavior for -0.0 and NaN.
23+
The sort order of doubles remains the same, however, with NaN at the end.
24+
Import Ordering.Double.IeeeOrdering to recover the previous behavior.
25+
See also https://www.scala-lang.org/api/current/scala/math/Ordering$$Double$.html.
26+
list.sorted
27+
^
28+
error: No warnings can be incurred under -Werror.
29+
3 warnings
30+
1 error

test/files/neg/t10511.scala renamed to test/files/neg/ordering-migration.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// scalac: -deprecation -Xfatal-warnings
1+
// scalac: -Xmigration -Werror
22
object Test {
33
val f = Ordering[Float]
44
val d = Ordering[Double]

test/files/neg/t10511.check

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)