Skip to content

Commit

Permalink
bug#10511 Add total orderings for Float and Double
Browse files Browse the repository at this point in the history
Add total orderings for Float and Double, so that there
are two implicit orderings for each in scope: one
consistent with a total ordering, and one consistent with
IEEE spec.
  • Loading branch information
NthPortal committed Mar 12, 2018
1 parent 7d6fc36 commit b69d410
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 48 deletions.
4 changes: 2 additions & 2 deletions src/library/scala/collection/immutable/NumericRange.scala
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,8 @@ object NumericRange {
Numeric.ByteIsIntegral -> Ordering.Byte,
Numeric.CharIsIntegral -> Ordering.Char,
Numeric.LongIsIntegral -> Ordering.Long,
Numeric.FloatAsIfIntegral -> Ordering.Float,
Numeric.DoubleAsIfIntegral -> Ordering.Double,
Numeric.FloatAsIfIntegral -> Ordering.FloatIeeeOrdering,
Numeric.DoubleAsIfIntegral -> Ordering.DoubleIeeeOrdering,
Numeric.BigDecimalAsIfIntegral -> Ordering.BigDecimal
)

Expand Down
9 changes: 4 additions & 5 deletions src/library/scala/math/Numeric.scala
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,8 @@ object Numeric {
def quot(x: Float, y: Float): Float = (BigDecimal(x) quot BigDecimal(y)).floatValue
def rem(x: Float, y: Float): Float = (BigDecimal(x) remainder BigDecimal(y)).floatValue
}
implicit object FloatIsFractional extends FloatIsFractional with Ordering.FloatOrdering
object FloatAsIfIntegral extends FloatAsIfIntegral with Ordering.FloatOrdering {
}
implicit object FloatIsFractional extends FloatIsFractional with Ordering.FloatIeeeOrdering
object FloatAsIfIntegral extends FloatAsIfIntegral with Ordering.FloatIeeeOrdering

trait DoubleIsConflicted extends Numeric[Double] {
def plus(x: Double, y: Double): Double = x + y
Expand Down Expand Up @@ -199,8 +198,8 @@ object Numeric {
implicit object BigDecimalIsFractional extends BigDecimalIsFractional with Ordering.BigDecimalOrdering
object BigDecimalAsIfIntegral extends BigDecimalAsIfIntegral with Ordering.BigDecimalOrdering

implicit object DoubleIsFractional extends DoubleIsFractional with Ordering.DoubleOrdering
object DoubleAsIfIntegral extends DoubleAsIfIntegral with Ordering.DoubleOrdering
implicit object DoubleIsFractional extends DoubleIsFractional with Ordering.DoubleIeeeOrdering
object DoubleAsIfIntegral extends DoubleAsIfIntegral with Ordering.DoubleIeeeOrdering
}

trait Numeric[T] extends Ordering[T] {
Expand Down
111 changes: 80 additions & 31 deletions src/library/scala/math/Ordering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,21 @@ trait LowPriorityOrderingImplicits {
*
* It contains many implicit orderings as well as well as methods to construct
* new orderings.
*
* @define doubleOrdering Because the behaviour of `Double`s specified by IEEE is
* not consistent with a total ordering when dealing with
* `NaN`, there are two orderings defined for `Double`:
* `DoubleTotalOrdering`, which is consistent with a total
* ordering, and `DoubleIeeeOrdering`, which is consistent
* as much as possible with IEEE spec and floating point
* operations defined in [[scala.math]].
* @define floatOrdering Because the behaviour of `Float`s specified by IEEE is
* not consistent with a total ordering when dealing with
* `NaN`, there are two orderings defined for `Float`:
* `FloatTotalOrdering`, which is consistent with a total
* ordering, and `FloatIeeeOrdering`, which is consistent
* as much as possible with IEEE spec and floating point
* operations defined in [[scala.math]].
*/
object Ordering extends LowPriorityOrderingImplicits {
@inline def apply[T](implicit ord: Ordering[T]) = ord
Expand Down Expand Up @@ -312,9 +327,38 @@ object Ordering extends LowPriorityOrderingImplicits {
}
implicit object Long extends LongOrdering

trait FloatOrdering extends Ordering[Float] {
outer =>
/** An ordering for `Float`s which is a fully consistent total ordering,
* and treats `NaN` as larger than all other `Float` values; it behaves
* the same as [[java.lang.Float.compare()]].
*
* $floatOrdering
*
* This ordering may be preferable for sorting collections.
*
* @see [[FloatIeeeOrdering]]
*/
trait FloatTotalOrdering extends Ordering[Float] {
def compare(x: Float, y: Float) = java.lang.Float.compare(x, y)
}
implicit object FloatTotalOrdering extends FloatTotalOrdering

/** An ordering for `Float`s which is consistent with IEEE specifications
* whenever possible.
*
* - `lt`, `lteq`, `equiv`, `gteq` and `gt` are consistent with primitive
* comparison operations for `Float`s, and return `false` when called with
* `NaN`.
* - `min` and `max` are consistent with `math.min` and `math.max`, and
* return `NaN` when called with `NaN` as either argument.
* - `compare` behaves the same as [[java.lang.Float.compare()]].
*
* $floatOrdering
*
* This ordering may be preferable for numeric contexts.
*
* @see [[FloatTotalOrdering]]
*/
trait FloatIeeeOrdering extends Ordering[Float] {
def compare(x: Float, y: Float) = java.lang.Float.compare(x, y)

override def lteq(x: Float, y: Float): Boolean = x <= y
Expand All @@ -324,25 +368,41 @@ object Ordering extends LowPriorityOrderingImplicits {
override def equiv(x: Float, y: Float): Boolean = x == y
override def max(x: Float, y: Float): Float = math.max(x, y)
override def min(x: Float, y: Float): Float = math.min(x, y)

override def reverse: Ordering[Float] = new FloatOrdering {
override def reverse = outer
override def compare(x: Float, y: Float) = outer.compare(y, x)

override def lteq(x: Float, y: Float): Boolean = outer.lteq(y, x)
override def gteq(x: Float, y: Float): Boolean = outer.gteq(y, x)
override def lt(x: Float, y: Float): Boolean = outer.lt(y, x)
override def gt(x: Float, y: Float): Boolean = outer.gt(y, x)
override def min(x: Float, y: Float): Float = outer.max(x, y)
override def max(x: Float, y: Float): Float = outer.min(x, y)

}
}
implicit object Float extends FloatOrdering
implicit object FloatIeeeOrdering extends FloatIeeeOrdering

trait DoubleOrdering extends Ordering[Double] {
outer =>
/** An ordering for `Double`s which is a fully consistent total ordering,
* and treats `NaN` as larger than all other `Double` values; it behaves
* the same as [[java.lang.Double.compare()]].
*
* $doubleOrdering
*
* This ordering may be preferable for sorting collections.
*
* @see [[DoubleIeeeOrdering]]
*/
trait DoubleTotalOrdering extends Ordering[Double] {
def compare(x: Double, y: Double) = java.lang.Double.compare(x, y)
}
implicit object DoubleTotalOrdering extends DoubleTotalOrdering

/** An ordering for `Double`s which is consistent with IEEE specifications
* whenever possible.
*
* - `lt`, `lteq`, `equiv`, `gteq` and `gt` are consistent with primitive
* comparison operations for `Double`s, and return `false` when called with
* `NaN`.
* - `min` and `max` are consistent with `math.min` and `math.max`, and
* return `NaN` when called with `NaN` as either argument.
* - `compare` behaves the same as [[java.lang.Double.compare()]].
*
* $doubleOrdering
*
* This ordering may be preferable for numeric contexts.
*
* @see [[DoubleTotalOrdering]]
*/
trait DoubleIeeeOrdering extends Ordering[Double] {
def compare(x: Double, y: Double) = java.lang.Double.compare(x, y)

override def lteq(x: Double, y: Double): Boolean = x <= y
Expand All @@ -352,20 +412,9 @@ object Ordering extends LowPriorityOrderingImplicits {
override def equiv(x: Double, y: Double): Boolean = x == y
override def max(x: Double, y: Double): Double = math.max(x, y)
override def min(x: Double, y: Double): Double = math.min(x, y)

override def reverse: Ordering[Double] = new DoubleOrdering {
override def reverse = outer
override def compare(x: Double, y: Double) = outer.compare(y, x)

override def lteq(x: Double, y: Double): Boolean = outer.lteq(y, x)
override def gteq(x: Double, y: Double): Boolean = outer.gteq(y, x)
override def lt(x: Double, y: Double): Boolean = outer.lt(y, x)
override def gt(x: Double, y: Double): Boolean = outer.gt(y, x)
override def min(x: Double, y: Double): Double = outer.max(x, y)
override def max(x: Double, y: Double): Double = outer.min(x, y)
}
}
implicit object Double extends DoubleOrdering

implicit object DoubleIeeeOrdering extends DoubleIeeeOrdering

trait BigIntOrdering extends Ordering[BigInt] {
def compare(x: BigInt, y: BigInt) = x.compare(y)
Expand Down
4 changes: 2 additions & 2 deletions src/library/scala/runtime/RichDouble.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ package scala
package runtime

final class RichDouble(val self: Double) extends AnyVal with FractionalProxy[Double] {
protected def num = scala.math.Numeric.DoubleIsFractional
protected def ord = scala.math.Ordering.Double
protected def num = scala.math.Numeric.DoubleIsFractional
protected def ord = scala.math.Ordering.DoubleTotalOrdering
protected def integralNum = scala.math.Numeric.DoubleAsIfIntegral

override def doubleValue() = self
Expand Down
2 changes: 1 addition & 1 deletion src/library/scala/runtime/RichFloat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ package runtime

final class RichFloat(val self: Float) extends AnyVal with FractionalProxy[Float] {
protected def num = scala.math.Numeric.FloatIsFractional
protected def ord = scala.math.Ordering.Float
protected def ord = scala.math.Ordering.FloatTotalOrdering
protected def integralNum = scala.math.Numeric.FloatAsIfIntegral

override def doubleValue() = self.toDouble
Expand Down
19 changes: 19 additions & 0 deletions test/files/neg/t10511.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
t10511.scala:2: error: ambiguous implicit values:
both object FloatTotalOrdering in object Ordering of type scala.math.Ordering.FloatTotalOrdering.type
and object FloatIeeeOrdering in object Ordering of type scala.math.Ordering.FloatIeeeOrdering.type
match expected type scala.math.Ordering[Float]
val f = Ordering[Float]
^
t10511.scala:3: error: ambiguous implicit values:
both object DoubleTotalOrdering in object Ordering of type scala.math.Ordering.DoubleTotalOrdering.type
and object DoubleIeeeOrdering in object Ordering of type scala.math.Ordering.DoubleIeeeOrdering.type
match expected type scala.math.Ordering[Double]
val d = Ordering[Double]
^
t10511.scala:6: error: ambiguous implicit values:
both object DoubleTotalOrdering in object Ordering of type scala.math.Ordering.DoubleTotalOrdering.type
and object DoubleIeeeOrdering in object Ordering of type scala.math.Ordering.DoubleIeeeOrdering.type
match expected type scala.math.Ordering[Double]
list.sorted
^
three errors found
7 changes: 7 additions & 0 deletions test/files/neg/t10511.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
object Test {
val f = Ordering[Float]
val d = Ordering[Double]

val list = List(1.0, 2.0, 3.0)
list.sorted
}
2 changes: 1 addition & 1 deletion test/files/presentation/infix-completion.check
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ askTypeCompletion at Snippet.scala(1,34)
retrieved 192 members
[inaccessible] protected def integralNum: math.Numeric.DoubleAsIfIntegral.type
[inaccessible] protected def num: math.Numeric.DoubleIsFractional.type
[inaccessible] protected def ord: math.Ordering.Double.type
[inaccessible] protected def ord: math.Ordering.DoubleTotalOrdering.type
[inaccessible] protected def unifiedPrimitiveEquals(x: Any): Boolean
[inaccessible] protected def unifiedPrimitiveHashcode(): Int
[inaccessible] protected[package lang] def clone(): Object
Expand Down
2 changes: 1 addition & 1 deletion test/files/presentation/infix-completion2.check
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ askTypeCompletion at Snippet.scala(1,34)
retrieved 211 members
[inaccessible] protected def integralNum: math.Numeric.DoubleAsIfIntegral.type
[inaccessible] protected def num: math.Numeric.DoubleIsFractional.type
[inaccessible] protected def ord: math.Ordering.Double.type
[inaccessible] protected def ord: math.Ordering.DoubleTotalOrdering.type
[inaccessible] protected def unifiedPrimitiveEquals(x: Any): Boolean
[inaccessible] protected def unifiedPrimitiveHashcode(): Int
[inaccessible] protected[package lang] def clone(): Object
Expand Down
2 changes: 2 additions & 0 deletions test/files/run/OrderingTest.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import scala.math.Ordering.DoubleTotalOrdering

object Test extends App {
def test[T](t1 : T, t2 : T)(implicit ord : Ordering[T]) = {
val cmp = ord.compare(t1, t2);
Expand Down
3 changes: 1 addition & 2 deletions test/files/run/t5857.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@


import scala.math.Ordering.DoubleIeeeOrdering

object Test {

Expand Down
Loading

0 comments on commit b69d410

Please sign in to comment.