Skip to content
Permalink
Browse files

Merge pull request #5761 from lrytz/sd329

Don't use `equals` for comparing java.lang.Double/Float
  • Loading branch information
adriaanm committed Mar 10, 2017
2 parents 680d866 + 6abb6ba commit 6e9268b08e813b6930165877fe6ce450890f0a9e
@@ -1264,14 +1264,22 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
def genEqEqPrimitive(l: Tree, r: Tree, success: asm.Label, failure: asm.Label, targetIfNoJump: asm.Label, pos: Position) {

/* True if the equality comparison is between values that require the use of the rich equality
* comparator (scala.runtime.Comparator.equals). This is the case when either side of the
* comparator (scala.runtime.BoxesRunTime.equals). This is the case when either side of the
* comparison might have a run-time type subtype of java.lang.Number or java.lang.Character.
* When it is statically known that both sides are equal and subtypes of Number of Character,
* not using the rich equality is possible (their own equals method will do ok.)
*
* When it is statically known that both sides are equal and subtypes of Number or Character,
* not using the rich equality is possible (their own equals method will do ok), except for
* java.lang.Float and java.lang.Double: their `equals` have different behavior around `NaN`
* and `-0.0`, see Javadoc (scala-dev#329).
*/
val mustUseAnyComparator: Boolean = {
val areSameFinals = l.tpe.isFinalType && r.tpe.isFinalType && (l.tpe =:= r.tpe)
!areSameFinals && platform.isMaybeBoxed(l.tpe.typeSymbol) && platform.isMaybeBoxed(r.tpe.typeSymbol)
platform.isMaybeBoxed(l.tpe.typeSymbol) && platform.isMaybeBoxed(r.tpe.typeSymbol) && {
val areSameFinals = l.tpe.isFinalType && r.tpe.isFinalType && (l.tpe =:= r.tpe) && {
val sym = l.tpe.typeSymbol
sym != BoxedFloatClass && sym != BoxedDoubleClass
}
!areSameFinals
}
}

if (mustUseAnyComparator) {
@@ -3,8 +3,8 @@ object Test {
val MinusZero = Float.box(-0.0f)
val PlusZero = Float.box(0.0f)

assert(PlusZero match { case MinusZero => false ; case _ => true })
assert(MinusZero match { case PlusZero => false ; case _ => true })
assert(PlusZero match { case MinusZero => true ; case _ => false })
assert(MinusZero match { case PlusZero => true ; case _ => false })
assert((MinusZero: scala.Float) == (PlusZero: scala.Float))
assert(!(MinusZero equals PlusZero))

@@ -0,0 +1,76 @@
object Test extends App {
def d1: Double = 0.0
def d2: Double = -0.0
def d3: Double = Double.NaN
def d4: Double = Double.NaN
assert(d1 == d2)
assert(d3 != d4)

def d1B: java.lang.Double = d1
def d2B: java.lang.Double = d2
def d3B: java.lang.Double = d3
def d4B: java.lang.Double = d4
assert(d1B == d2B)
assert(d1 == d1B)
assert(d1B == d1)
assert(d3B != d4B)
assert(d3 != d4B)
assert(d3B != d4)

assert(!d1B.equals(d2B)) // ! see javadoc
assert( d3B.equals(d4B)) // ! see javadoc

def d1A: Any = d1
def d2A: Any = d2
def d3A: Any = d3
def d4A: Any = d4
assert(d1A == d2A)
assert(d1 == d1A)
assert(d1A == d1)
assert(d1B == d1A)
assert(d1A == d1B)

assert(d3A != d4A)
assert(d3 != d4A)
assert(d3A != d4)
assert(d3B != d4A)
assert(d3A != d4B)


def f1: Float = 0.0f
def f2: Float = -0.0f
def f3: Float = Float.NaN
def f4: Float = Float.NaN
assert(f1 == f2)
assert(f3 != f4)

def f1B: java.lang.Float = f1
def f2B: java.lang.Float = f2
def f3B: java.lang.Float = f3
def f4B: java.lang.Float = f4
assert(f1B == f2B)
assert(f1 == f1B)
assert(f1B == f1)
assert(f3B != f4B)
assert(f3 != f4B)
assert(f3B != f4)

assert(!f1B.equals(f2B)) // ! see javadoc
assert( f3B.equals(f4B)) // ! see javadoc

def f1A: Any = f1
def f2A: Any = f2
def f3A: Any = f3
def f4A: Any = f4
assert(f1A == f2A)
assert(f1 == f1A)
assert(f1A == f1)
assert(f1B == f1A)
assert(f1A == f1B)

assert(f3A != f4A)
assert(f3 != f4A)
assert(f3A != f4)
assert(f3B != f4A)
assert(f3A != f4B)
}

0 comments on commit 6e9268b

Please sign in to comment.
You can’t perform that action at this time.